1 Objective

The objective of this analysis is to identify potential data quality issues with in coming Kenya soil health round 1 study data so that the enumeration team can address issues in the field and improve overall quality. This file will also serve as (1) an initial cleaning file and (2) an attrition and balance check to inform when to wrap up enumeration.

2 Key Takeaways

  1. We have likely outliers in the numeric data. Follow up with enumerators about these values.
  2. Many round 1 GPS points are not close to the baseline value. Follow up about plot continuity and GPS quality. (see Kenya round 1 data check)
rm(list = ls())
cat("\014")

## set up some global options
# always set stringsAsFactors = F when loading data
options(stringsAsFactors=FALSE)
# show the code
knitr::opts_chunk$set(echo = TRUE)
# define all knitr tables to be html format
options(knitr.table.format = 'html')
# change code chunk default to not show warnings or messages
knitr::opts_chunk$set(warning = FALSE, message = FALSE)
libs <- c("dplyr", "reshape2", "knitr", "ggplot2", "tibble", "readxl", 
    "MASS", "gridExtra", "cowplot", "robustbase", "car", "RStata", "foreign",
    "tidyr", "readxl", "stringr", "sp", "dismo", "leaflet", "XML", "ggmap", "knitr",
    "MASS")
lapply(libs, require, character.only = T, quietly = T, warn.conflicts = F)
[[1]]
[1] TRUE

[[2]]
[1] TRUE

[[3]]
[1] TRUE

[[4]]
[1] TRUE

[[5]]
[1] TRUE

[[6]]
[1] TRUE

[[7]]
[1] TRUE

[[8]]
[1] TRUE

[[9]]
[1] TRUE

[[10]]
[1] TRUE

[[11]]
[1] TRUE

[[12]]
[1] TRUE

[[13]]
[1] TRUE

[[14]]
[1] TRUE

[[15]]
[1] TRUE

[[16]]
[1] TRUE

[[17]]
[1] TRUE

[[18]]
[1] TRUE

[[19]]
[1] TRUE

[[20]]
[1] TRUE

[[21]]
[1] TRUE

[[22]]
[1] TRUE

[[23]]
[1] TRUE
#### define helpful functions
# define function to adjust table widths
html_table_width <- function(kable_output, width) {
  width_html <- paste0(paste0('<col width="', width, '">'), collapse = "\n")
  sub("<table>", paste0("<table>\n", width_html), kable_output)
}
source("../oaflib/commcareExport.R")
source("../oaflib/misc.R")

3 Data

3.1 Import round 1 data

I’m going to use the CommCare API created by Robert On to access the data directly from CommCare. This ensures that there are no changes to the data between the point of access and the point of cleaning and analysis

forceUpdateAll = F
forceUpdate = forceUpdateAll
d <- getFormData("harvest", "Soil Sampling", "Soil Sampling 2017", forceUpdate)
[1] "found 677cf904ae87a358b18d0ad453b747d"
Duplicated column names deduplicated: 'District Name' => 'District Name_1' [20], 'Site Name' => 'Site Name_1' [21], 'Last Name' => 'Last Name_1' [22], 'First Name' => 'First Name_1' [23], 'Client Phone' => 'Client Phone_1' [24], 'What seed type did you use on this plot?' => 'What seed type did you use on this plot?_1' [53], 'How many kgs of seed did you use?' => 'How many kgs of seed did you use?_1' [54], 'Measurement of the reported yield/quantity' => 'Measurement of the reported yield/quantity_1' [56], 'What fertilizer did you use on your ____ for the long rains 2016?' => 'What fertilizer did you use on your ____ for the long rains 2016?_1' [63], 'How many KGs of CAN did you apply to the ____?' => 'How many KGs of CAN did you apply to the ____?_1' [65], 'How many KGs of NPK did you apply to the ____?' => 'How many KGs of NPK did you apply to the ____?_1' [66], 'How many KGs of urea did you apply to the ____?' => 'How many KGs of urea did you apply to the ____?_1' [67]Parsed with column specification:
cols(
  .default = col_character(),
  metadata.deviceID = col_double(),
  metadata.timeStart = col_datetime(format = ""),
  metadata.timeEnd = col_datetime(format = ""),
  `Are you or household member a member of OAF 2017?` = col_integer(),
  `OAF ID` = col_integer(),
  `Respondent's approximate age` = col_integer(),
  `Respondent's Gender` = col_integer(),
  `Number of people in this Household(Include respondent).` = col_integer(),
  `How many seasons have you participated with One Acre Fund, including this long rains 2017 season?` = col_integer(),
  `How many cows do you own?` = col_integer(),
  `How many goats do you own?` = col_integer(),
  `How many chickens do you own?` = col_integer(),
  `How many pigs do you own?` = col_integer(),
  `How many sheep do you own?` = col_integer(),
  `Please choose the closest field size` = col_double(),
  `How many kgs of seed did you use?` = col_double(),
  `What was the yield for this plot?` = col_double(),
  `How would you describe your long rains 2016 harvest in this field compared to previous long rains seasons?` = col_integer(),
  `How many kgs of seed did you use?_1` = col_double(),
  `What was the yield for this intercrop?` = col_double()
  # ... with 12 more columns
)
See spec(...) for full column specifications.
# add in force update to get latest report

3.2 Import baseline

baselineDir <- normalizePath(file.path("..", "ke_shs_baseline", "data"))
b <- read.csv(file=paste0(baselineDir, "/shs ke baseline.csv")) # obj d

3.3 Import soil data

Here I access the soil predictions from the OAF soil lab. Patrick Bell manages the lab and Mike Barber oversees the prediction scripts.

soilDir <- normalizePath(file.path("..", "..", "data", "OAF Soil Lab Folder", "Projects", "ke_shs_17", "4_predicted", "other_summaries"))
soil <- read.csv(file=paste(soilDir, "combined-predictions-including-bad-ones.csv", sep = "/"))
idDir <- normalizePath(file.path("..", "..", "data", "OAF Soil Lab Folder", "Projects", "ke_shs_17", "5_merged"))
Identifiers <- read_excel(paste(idDir,"Soil Sampling - Sample reception 2017 - Sample reception 20172017-04-12 (1).xlsx",sep="/"), sheet=1) %>%
  mutate(
    reception_barcode = gsub("_", "", reception_barcode)
  )
soil$SSN[!soil$SSN %in% Identifiers$reception_barcode]
[1] "oafke002126"
Identifiers$reception_barcode[!Identifiers$reception_barcode %in% soil$SSN]
[1] "oafke0001485" "oafke0002126"

It seems to be the case that one barcode is not matching due to the number of zeros. Correct and update.

soil$SSN[soil$SSN=="oafke002126"] <- "oafke0002126"

There’s one record in the Identifiers that doesn’t appear in the data. An extra sample without a survey? Seems odd.

3.4 Update variable names

The variable names are out of control. In order to make the variable names more user friendly, I went to CommCare and downloaded the form contents that includes the variable name. I then wrote in a more user friendly variable name in the file. I import and overwrite the variable names here. Perhaps there’s a better way to do this but it’s easier to log the changes in excel than to write out all the code. The reference files will be on hand in case I accidentally mislabel a variable. The variable names in the excel file start with variable 11 from CommCare. The first 10 CommCare variables are meta data which we will keep.

General TODO

  • import round 1 soil data
  • do some cleaning and checking of round 1 data
  • import baseline data
  • align baseline and round 1 data long
newName <- read_excel("var_names.xlsx", sheet=1)
qTypes <- c("Multiple Choice", "Phone Number or Numeric ID", "Checkbox", "Text", "Decimal", "Image Capture", "Barcode Scan", "GPS", "Integer")
newName <- newName %>% dplyr::select(1:4
) %>% dplyr::filter(newName$Type %in% qTypes) %>% as.data.frame()
newName <- newName %>% filter(new.var.name!="general.comment")

Keep the participation variable in d so we understand who agreed to participate. Make additional changes to d to prepare it to be combined with the meta information from CommCare.

names(d)[26] <- "participation"
names(d)[names(d)=="#form/id_base/Soil_Sample_Id"] <- "Soil_Sample_Id"
names(d)[names(d)=="#form/id_2017new/Soil_Sample_Id"] <- "New_soil_sample_id"

Merge in variable names to check that they match and then replace names(). I’m subtracting two because the Soil_Sample_Id variables appear out of order.

I’m also adding some variables to the new name vector as d now has a different variable length

newNameVec <- c(newName$new.var.name[1], "agree", "interviewed.2016", "refusal", newName$new.var.name[2:(length(newName$new.var.name)-1)], "comment", newName$new.var.name[length(newName$new.var.name)])
names(d)[11:(length(d)-3)] <- newNameVec
# drop vars with drop
d <- d[,-which(grepl("drop.", names(d)))]

Make new variables

#convert acreage to m2 and then calculate fertilizer per acre
d$plot.m2 <- d$plot.size*4046
m2ToAcres <- function(input, meters) {
  res <- (input/meters)*4046
  return(res)
}
d$can.acre <- m2ToAcres(d$can.main, d$plot.m2)
d$dap.acre <- m2ToAcres(d$dap.main,d$plot.m2)
d$npk.acre <- m2ToAcres(d$npk.main,d$plot.m2)
d$urea.acre <- m2ToAcres(d$urea.main,d$plot.m2)
d$compost.acre <- m2ToAcres(d$compost.kgs,d$plot.m2)
d$seed.acre <- m2ToAcres(d$seed.kgs,d$plot.m2)
d$intercrop.seed.acre <- m2ToAcres(d$intercrop.seed.kgs, d$plot.m2)
d$intercrop.dap.acre <- m2ToAcres(d$dap.intercrop, d$plot.m2)
d$intercrop.can.acre <- m2ToAcres(d$can.intercrop, d$plot.m2)
d$intercrop.npk.acre <- m2ToAcres(d$npk.intercrop, d$plot.m2)
d$intercrop.urea.acre <- m2ToAcres(d$urea.intercrop, d$plot.m2)

Yield calculation - first let’s confirm that when the yield.unit is NA that there isn’t a yield value.

table(d$yield[is.na(d$yield.unit)], useNA = 'ifany')

   0 <NA> 
  24   56 

TODO - Charles: I assume a 0 yield is a true value. Or should it be NA? The code below converts all yield values associated with an NA unit to NA when in fact there should be some 0s.

d$yield.kg <- ifelse(d$yield.unit=="100kg", d$yield*100, 
              ifelse(d$yield.unit=="50kg", d$yield*50,
              ifelse(d$yield.unit=="90kg", d$yield*90,
              ifelse(d$yield.unit=="GG", d$yield*2, NA))))

3.4.1 Import feedback

Here I take the follow up responses from enumerators and update the values in the the full data. I only want the outlier checks to show values that we haven’t already addressed through soliciting additional feedback from enumerators and farmers. This code assumes the data comes in with rows as farmers and columns as the questions that needed to be addressed. I’ve reshaped it to include

TODO - come back and figure out how to quickly add in the phone data

#updateDir <- paste(getwd(), "enum_summaries", sep = "/")
# updates <- read.csv("Phone calls survey.csv", header=T, stringsAsFactors = F) %>% rename(
#   Soil_Sample_Id = soil_sample_id,
#   age = Age.of.respondent,
#   can.intercrop = How.many.KGs.of.CAN.did.you.apply.to.the..intercrop.,
#   can.main = How.many.KGs.of.CAN.did.you.apply.to.the.main.crop.,
#   chickens = Number.of.chickens.How.many.chickens.do.you.own..chickens,
#   dap.intercrop = How.many.KGs.of.DAP.did.you.apply.to.the.intercrop.,
#   dap.main = How.many.KGs.of.DAP.did.you.apply.to.the.main.crop.,
#   hhsize = Number.of.people.in.this.Household.Include.respondent..,
#   intercrop.seed.kgs = How.many.kgs.of.seed.did.you.use.in.your.intercrop.,
#   lime.kgs = How.many.KGs.of.lime.did.you.use.on.this.plot.,
#   npk.main = How.many.KGs.of.NPK.did.you.apply.to.the.maincrop.,
#   plot.size = What.is.your.aproximate.plotsize,
#   seed.kgs = How.many.kgs.of.seed.did.you.use.in.the.Maincrop.,
#   sheep = How.many.sheep.do.you.own.,
#   urea.main = How.many.KGs.of.urea.did.you.apply.to.the.maincrop.,
#   yield = What.was.the.yield.for.this.plot.main.crop.
# ) %>% mutate(
#   yield = ifelse(yield=="N/A", NA,  ifelse(grepl("Not", yield), NA, yield)),
#   yield = ifelse(yield=="32 bags of 90 kgs", "32 bags", yield)
# )
# 
# updates$yield.unit <- sapply(updates$yield, function(x){ # separate out the unit
#   strsplit(x, " ")[[1]][2]})
# 
# updates$yield.kg <- sapply(updates$yield, function(x){ # keep only the number
#   strsplit(x, " ")[[1]][1]})
# 
# updates$yield.kg <- ifelse(grepl("bag",  updates$yield.unit), as.numeric(updates$yield)*90, ifelse(grepl("goro", updates$yield.unit),  as.numeric(updates$yield)*2.45, updates$yield))
# 
# 
# updates <- melt(updates, id.vars = c("Soil_Sample_Id", "Comments"), measure.vars = names(updates)[6:19])
# updates <- updates %>% filter(!is.na(value) | value!= " ")
# check that this works:
#print(updates[1,])

3.5 Merge soil into data

TODO - check into how many merges we’re not getting. Finalize merge report.

d$sampling.barcode <- gsub("_", "", d$sampling.barcode)
names(soil)[names(soil)=="SSN"] <- "sampling.barcode"
d <- left_join(d, soil, by="sampling.barcode")
soilVars <- names(soil)[2:21]

3.6 Import soil texture data

I guess I didn’t do that earlier so import the soil texture data now.

text <- read_xlsx("soil_texture.xlsx") %>%
  mutate(reception_barcode = gsub("_", "", reception_barcode)) %>%
  rename(sampling.barcode = reception_barcode,
         soil.texture = `soil texture`)
d <- d %>%
  left_join(., text[,c("sampling.barcode", "soil.texture")], by = "sampling.barcode")

3.7 Outlier check

Check all numeric variables for outliers. First, it’s probably safe to replace all -99s with NA

TODO - set up outlier checks

catVars <- names(d)[sapply(d, function(x){
  is.character(x)
})]
enumClean <- function(dat, x, toRemove){
  dat[,x] <- ifelse(dat[,x] %in% toRemove, NA, dat[,x])
  return(dat[,x])
}
strTable <- function(dat, x){
  varName = x
  tab = as.data.frame(table(dat[,x], useNA = 'ifany'))
  tab = tab[order(tab$Freq, decreasing = T),]
  end = ifelse(length(tab$Var1)<10, length(tab$Var1), 10)
  repOrder = paste(tab$Var1[1:end], collapse=", ")
  out = data.frame(variable = varName,
                   responses = repOrder)
  
  return(out)
}
# clean up known values
catEnumVals <- c("-99", "-88", "- 99", "-99.0", "88", "_88", "- 88", "0.88",
                 "--88", "__88", "-88.0", "99.0")
d[,catVars] <- sapply(catVars, function(y){
  d[,y] <- enumClean(d,y, catEnumVals)
})
responseTable <- do.call(rbind, lapply(catVars, function(x){
  strTable(d, x)
}))

3.7.1 Categorical response table

A simple table to preview the values in the data. The values are ranked by frequency.

kable(responseTable, format="markdown")
variable responses
AppFormId /Users/mlowes/Google Drive/data/commcare_cache/677cf904ae87a358b18d0ad453b747d
id 000ce8ff-56af-4af1-8ab3-dcaaa54fe230, 0011522b-761a-45a4-9783-c217f1e7d4d6, 00135a79-0c27-4b51-89c5-fbd6d5494e06, 0033f926-d5aa-40d5-9279-0bba42d3189b, 00723ce4-7aae-4b17-924c-54885d57a8a0, 008b1760-6518-489a-902b-c6b5d8673cf0, 00905142-2f7a-4231-a32b-0c6f9fb51261, 00b06cd5-846b-4e1b-9ff8-3fd4d7f5a37d, 00b8ff5b-32f8-4909-85e6-a81aaf2c399e, 00bc4021-2420-41c5-838f-2b97646bd24b
domain harvest
date_header NA
metadata.userID 280c99abbb2534baa8bd95c41c7fb47d, 036a641a60414b75155e0e4fefdf01bb, 74acd8230c1733e972169cfc7113229b, d1cb024846d35c496780e0a681cb9f92, d1cb024846d35c496780e0a681cc03c9, 942a528e8cc2dc1f7f060d152839394a, d1cb024846d35c496780e0a681cca808, bc1170b650390a876f8abb9571669788, 2ed6f14f0f5a764c0d911419565ee8ff, 28ccf07370af4faa5f15ecd64bd21c1c
metadata.username eric.k, rachel.g, john.s, stephen.n, lazarus.m, clinton.n, dennis.k, gibson.r, florence.o, wallace.n
form.case.@case_id 43abb59751f94d51acf171d595651fa0, 548a04af36fc4790a272abcfb0c1c7e9, 54ecedd1d1074986a23d2c777d127fd3, 6fb5ac28647742e598e104db43d5b300, 8bd82fadf72542b282443cdb2e1f735b, ef006c5dc2e64d93a7b27c3aefe50009, 000e81e8014a4b8bb569f7616b3f823e, 00329d4fe2304e278c438a73757cbc7c, 0042a665dafd481d9d4f6aac8cd19b2d, 005b4ac31e5e422387906b0910c15eba
same.hh Yes, No, NA
agree NA, yes, no
interviewed.2016 NA, No, Yes
refusal NA, refused
region Western Province, Nyanza, NA, Nyanza1, Western Provinc, Western Province's, Western ProvinceE, Western Provincer
district Gem, Butere, Rachuonyo, Kakamega (South), Lugari, Migori, Kakamega B (North), Rongo, Kisii, Busia
site NA, Kamroth Lower, Kokwanyo West, Sigomere, lureko, Achuth, Kajulu, emangale, Kanyasrega, Kisatiru
phone NA, 0, 0717356420, 0701313242, 0703490814, 0705337016, 0719642096, 0726211155, 0792051319, 712115910
district1 NA, Rongo, Rachuonyo, Kakamega B (North), Lugari, Suneka, Chwele, Gem, Matete, Busia
site1 NA, Kamuga, Nyang'inja, riana, Bunjosi, Burinda, Chekalini, Gongo, IKURUMA, Isongo
participation consents, NA
which.seasons NA, lr2016, lr2016 lr2015, lr2016 lr2015 lr2014, lr2016 lr2015 lr2014 lr2013, lr2016 lr2015 lr2014 lr2013 lr2012, lr2016 lr2015 lr2014 lr2013 lr2012 lr2011, lr2016 lr2015 lr2014 lr2013 lr2012 lr2011 lr2010, lr2015, lr2016 lr2014
plot.location The plot is behind the farmers house, The plot is opposite the homestead of the farmer, Besides the farmer's house, behind farmers house, The plot is behind the farmer's homestead, The plot is besides the farmers house, The plot is immediately behind the farmer's house., The plot is infront of the farmers house, The plot is just behind the farmers house, behind farmers house.
own Yes, No, NA
relation.land NA, renting, borrowing, leasing
own.2016 Yes, No, NA
most.farming Female, both, Male, NA
main.crop maize, NA, fallow, other, sorghum, sugar, beans, sukuma, groundnuts
main.crop.specify NA, Nappier grass, tobacco, cassava, cassavas, Napier grass, nappier grass, Pineapple, Tobacco
seed.type hybrid, local, NA
yield.unit 90kg, GG, 100kg, 50kg, NA
intercrop beans, 0, NA, groundnuts, soya, maize, sorghum, sugar, other, sweetpotatoes
intercrop.specify NA, cassava
intercrop.seed.type local, NA, hybrid
intercrop.yield.unit GG, NA, 90kg, 100kg, 50kg
fertilizer.main dap can, dap, none, NA, dap urea, dap can urea, can, can npk, can urea, other
fertilizer.intercrop none, NA, dap, dap can, can, other, dap none, urea, npk, dap other
compost.quality NA, good, average, poor
compost.type NA, cow, plant_residue, goat, chicken, kitchen_waste, pig
lime no, NA, yes_oaf, yes_outside
fertility.comp average, morefertile, lessfertile, NA
field.location hillside, valley, hilltop, NA
soil.type loam, sandy, clay, NA
soil.color brown, black, red, NA
erosion 0, napier, grad_terrace, drainage, radical_terrace, NA
fallow.yn No, Yes, NA, dont_know
fallow.cover NA, weeds, grasses, legumes, mixed_grasses&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;legumes, Bare_fallow, mixed_grasses&amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;amp;legumes, mixed_grasses&amp;amp;amp;amp;amp;amp;amp;amp;amp;legumes
residues planted_into_residues, fed_to_livestock, no_incorporation, incorporated_by_hand, sold_residue, NA
photo NA, 1325379545882.jpg, 1325379607350.jpg, 1325380848958.jpg, 1486093019962.jpg, 1486096823433.jpg, 1486097641286.jpg, 1486176674776.jpg, 1486177274444.jpg, 1486182820463.jpg
sampling.barcode oafke0000321, oafke0000325, oafke0000661, oafke0000940, oafke0000966, oafke0001085, oafke0002000, oafke0002265, oafke0000001, oafke0000002
comment NA, No comment, N/A, no comment, none, No comments, non, no comments, 0, no
gps -0.5056573852841059 34.73783778147609 1390.63 15.0, -0.6098935 34.6105897 0.0 3669.0, -0.5056573852841059 34.73783778147609 1390.63 10.0, -0.5497645 34.6277074 0.0 4821.0, -0.5843478 34.6279001 0.0 4799.0, -0.7390152 34.5656951 0.0 4553.0, -0.5504687967967642 34.73721033097699 1509.32 15.0, -0.5906037 34.7379935 0.0 3000.0, -0.598296 34.7092347 0.0 4807.0, -0.6102046 34.5573404 0.0 3965.0
#form/Sampling2017_Complete Complete, NA
Soil_Sample_Id NA, Butere61451, c1019, Kisii43379, Busia11633, Busia11638, Busia11913, Busia12588, Busia12982, Busia13182
New_soil_sample_id NA, Busia25200, Butere15379, c1021, c1106, c1126, c1138, c1140, c1149, c1176
soil.texture Clay loam, Sandy clay loam, clay loam, silty clay loam, Silty clay loam, Silty clay, Loam, Silty Clay loam, sandy clay loam, Sandy loam

3.7.2 Categorical response graphs

repGraphs <- function(dat, x){
  tab = as.data.frame(table(dat[,x], useNA = 'ifany'))
  tab = tab[order(tab$Freq, decreasing = T),]
  print(
    ggplot(data=tab, aes(x=Var1, y=Freq)) + geom_bar(stat="identity") +
      theme(legend.position = "bottom", axis.text.x = element_text(angle = 45, hjust = 1)) +
      labs(title =paste0("Composition of variable: ", x))
  )
}
adminVars <- c(names(d)[grep("meta", names(d))],"enum_name", "photo",  "participation", "refusal", "phone",  "comment", "gps", "Soil_Sample_Id", "sampling.barcode", "id", "domain", "date_header", "form.case.@case_id", "site", "district1", "site1", "plot.location", "New_soil_sample_id", "#form/Sampling2017_Complete", "AppFormId")
nonAdminVars <- catVars[!catVars %in% adminVars]
for(i in 1:length(nonAdminVars)){
  repGraphs(d, nonAdminVars[i])
}

3.8 Numeric variables

numVars <- names(d)[sapply(d, function(x){
  is.numeric(x)
})]

Basic cleaning of known issues like enumerator codes for DK, NWR, etc.

enumVals <- c(-88,-85, -99)
d[,numVars] <- sapply(numVars, function(y){
  d[,y] <- enumClean(d,y, enumVals)
})

3.8.1 Numeric outlier table

iqr.check <- function(dat, x) { 
  q1 = summary(dat[,x])[[2]]
  q3 = summary(dat[,x])[[5]] 
  iqr = q3-q1
  mark  = ifelse(dat[,x] < (q1 - (1.5*iqr)) | dat[,x] > (q3 + (1.5*iqr)), 1,0)
  tab = rbind(
    summary(dat[,x]),
    summary(dat[mark==0, x])
  )
  return(tab)
}
# remove admin vars
numAdminVars <- c("metadata.deviceID", "oafid", "X")
numVarsNotAdmin <- numVars[!numVars %in% numAdminVars]
iqrTab <- do.call(plyr::rbind.fill, lapply(numVarsNotAdmin, function(y){
  #print(y)
  res = iqr.check(d, y)
  #print(dim(res))
  out = data.frame(var=rbind(y, paste(y, ".iqr", sep="")), res)
  return(out)
}))
iqrTab[,2:8] <- sapply(iqrTab[,2:8], function(x){round(x,1)})

The outlier table summarizes the numeric variables with and without IQR outliers to show how the data changes based on this filter.

TODO - check the composition of the fertilizer/acre variables.

knitr::kable(iqrTab, row.names = F, digits = 0, format = 'markdown')
var Min. X1st.Qu. Median Mean X3rd.Qu. Max. NA.s
oaf 0 0 0 0 1 1 2
oaf.iqr 0 0 0 0 1 1 2
age 5 35 45 101 56 63820 6
age.iqr 5 35 45 46 56 87 6
respondent.gender 0 0 1 1 1 1 2
respondent.gender.iqr 0 0 1 1 1 1 2
hhsize 0 4 6 6 7 66 2
hhsize.iqr 0 4 6 6 7 11 2
seasons.oaf 0 0 1 2 3 9 2
seasons.oaf.iqr 0 0 1 2 3 7 2
cows 0 0 2 2 3 20 1
cows.iqr 0 0 2 2 3 7 1
goats 0 0 0 1 0 14 1
goats.iqr 0 0 0 0 0 0 1
chickens 0 3 6 9 11 300 1
chickens.iqr 0 3 6 7 10 23 1
pigs 0 0 0 0 0 30 8
pigs.iqr 0 0 0 0 0 0 8
sheep 0 0 0 0 0 25 1
sheep.iqr 0 0 0 0 0 0 1
plot.size 0 0 0 0 0 2 2
plot.size.iqr 0 0 0 0 0 1 2
seed.kgs 0 3 4 5 6 100 56
seed.kgs.iqr 0 3 4 4 6 10 56
yield 0 2 5 17 9 1120 56
yield.iqr 0 2 4 5 7 19 56
harvestcomp.2016 1 1 2 2 2 3 48
harvestcomp.2016.iqr 1 1 2 2 2 3 48
intercrop.seed.kgs 0 2 4 5 6 60 810
intercrop.seed.kgs.iqr 0 2 4 5 6 12 810
intercrop.yield 0 3 8 12 20 104 812
intercrop.yield.iqr 0 2 8 11 20 45 812
intercrop.harvestcomp 1 1 2 2 2 3 805
intercrop.harvestcomp.iqr 1 1 2 2 2 3 805
dap.main 0 10 25 23 25 200 240
dap.main.iqr 0 10 16 18 25 46 240
can.main 0 12 25 24 25 200 677
can.main.iqr 0 10 25 18 25 42 677
npk.main 3 20 50 37 50 100 1924
npk.main.iqr 3 16 38 32 50 50 1924
urea.main 1 4 6 12 10 100 1878
urea.main.iqr 1 4 5 5 8 12 1878
dap.intercrop 0 5 10 13 12 125 1658
dap.intercrop.iqr 0 4 8 7 10 20 1658
can.intercrop 0 5 8 12 12 125 1856
can.intercrop.iqr 0 4 6 7 10 20 1856
npk.intercrop 25 31 38 38 44 50 1935
npk.intercrop.iqr 25 31 38 38 44 50 1935
urea.intercrop 0 2 3 3 4 5 1933
urea.intercrop.iqr 0 2 3 3 4 5 1933
compost.kgs 0 0 0 77 80 12000 54
compost.kgs.iqr 0 0 0 33 50 200 54
lime.kgs 4 10 12 29 25 150 1901
lime.kgs.iqr 4 9 12 15 23 38 1901
slope 0 5 5 7 10 45 2
slope.iqr 0 5 5 6 10 17 2
plot.m2 253 1012 2023 2113 2023 8092 2
plot.m2.iqr 253 1012 2023 1578 2023 3034 2
can.acre -132 32 50 47 50 200 677
can.acre.iqr 5 25 50 41 50 77 677
dap.acre -132 32 50 47 50 400 240
dap.acre.iqr 5 25 50 40 50 77 240
npk.acre 6 33 50 62 100 200 1924
npk.acre.iqr 6 33 50 62 100 200 1924
urea.acre 1 8 16 21 24 100 1878
urea.acre.iqr 1 8 16 15 20 40 1878
compost.acre -1584 0 0 187 180 48000 37
compost.acre.iqr -198 0 0 61 80 450 37
seed.acre -79 8 10 12 12 640 55
seed.acre.iqr 3 8 9 10 12 18 55
intercrop.seed.acre -79 7 9 12 16 120 809
intercrop.seed.acre.iqr 0 6 8 10 14 30 809
intercrop.dap.acre -198 10 20 25 40 100 1657
intercrop.dap.acre.iqr 0 10 20 24 40 80 1657
intercrop.can.acre -132 8 20 24 40 100 1855
intercrop.can.acre.iqr 0 9 20 24 37 64 1855
intercrop.npk.acre 25 31 38 38 44 50 1935
intercrop.npk.acre.iqr 25 31 38 38 44 50 1935
intercrop.urea.acre 0 6 9 8 12 16 1933
intercrop.urea.acre.iqr 0 6 9 8 12 16 1933
yield.kg 4 135 300 489 540 40000 80
yield.kg.iqr 4 135 270 340 495 1120 80
pH 0 5 6 6 6 9 34
pH.iqr 4 5 5 6 6 6 34
Phosphorus 2 7 10 Inf 15 Inf 34
Phosphorus.iqr 2 7 10 11 14 28 34
Potassium 31 102 147 Inf 208 Inf 34
Potassium.iqr 31 101 144 158 202 365 34
Calcium 0 596 897 1032 1285 9102 34
Calcium.iqr 0 587 871 944 1221 2311 34
Sulphur 4 9 11 Inf 12 Inf 34
Sulphur.iqr 5 9 11 11 12 16 34
Boron 0 0 0 0 0 1 34
Boron.iqr 0 0 0 0 0 1 34
Zinc 2 5 6 Inf 8 Inf 34
Zinc.iqr 2 5 6 6 8 12 34
X.Sodium 0 4 7 Inf 12 Inf 34
X.Sodium.iqr 0 4 6 8 11 24 34
X.C.E.C 0 8 10 11 14 39 34
X.C.E.C.iqr 0 8 10 11 13 22 34
X.EC..Salts. 36 63 74 Inf 88 Inf 34
X.EC..Salts..iqr 36 63 74 76 87 125 34
Magnesium 0 104 152 174 223 996 34
Magnesium.iqr 0 103 149 166 216 399 34
Iron 77 129 144 Inf 163 Inf 34
Iron.iqr 81 128 144 145 161 213 34
X.Phosphorus.Sorption.Index..PSI. 13 83 116 Inf 162 Inf 34
X.Phosphorus.Sorption.Index..PSI..iqr 13 82 114 122 156 278 34
X.Organic.Carbon 0 2 2 2 2 3 34
X.Organic.Carbon.iqr 1 2 2 2 2 3 34
Manganese 45 159 251 245 323 500 34
Manganese.iqr 45 159 251 245 323 500 34
Copper 1 2 4 4 5 11 34
Copper.iqr 1 2 4 4 5 10 34
Aluminium 476 931 1063 1038 1164 1569 34
Aluminium.iqr 583 942 1067 1047 1165 1504 34
X.Exchangeable.Aluminium 0 0 0 0 0 1 34
X.Exchangeable.Aluminium.iqr 0 0 0 0 0 1 34
X.Exchangeable.Acidity 0 0 0 0 0 1 34
X.Exchangeable.Acidity.iqr 0 0 0 0 0 1 34
X.Total.Nitrogen 0 0 0 0 0 0 34
X.Total.Nitrogen.iqr 0 0 0 0 0 0 34

3.8.2 Outlier Graphs

# http://rforpublichealth.blogspot.com/2014/02/ggplot2-cheatsheet-for-visualizing.html
for(i in 1:length(numVarsNotAdmin)){
    base <- ggplot(d, aes(x=d[,numVarsNotAdmin[i]])) + labs(x = numVarsNotAdmin[i])
    temp1 <- base + geom_density()
    temp2 <- base + geom_histogram()
    #temp2 <- boxplot(r[,numVars[i]],main=paste0("Variable: ", numVars[i]))
    multiplot(temp1, temp2, cols = 2)
}

3.8.3 Clean soil values

Before aligning them with the baseline soil values

check.3sd <- function(x) {
  x = ifelse(is.infinite(x), NA, x)
  mean = mean(x, na.rm=T)
  sd = sd(x, na.rm=T)
  mark = ifelse(x>(mean + (3*sd)) |
        x<(mean - (3*sd)), NA, x)
  return(mark)
}
sdSoilVals <- d %>%
  dplyr::select(pH:X.Total.Nitrogen) 
sdCheck <- as.data.frame(apply(sdSoilVals, 2, function(x){
  return(check.3sd(x))
}))
for(i in 1:length(soilVars)){
  print(ggplot(data=sdCheck, aes(x=sdCheck[,soilVars[i]])) + 
    geom_density() + 
    labs(x=soilVars[i])
  )
}

Important note: I’m going to add the adjusted values to the r data frame giving the previous variables the extension .raw so I can distinguish between the original and modified data.

names(d)[which(names(d)=="pH"):which(names(d)=="X.Total.Nitrogen")] <- paste0(names(d)[which(names(d)=="pH"):which(names(d)=="X.Total.Nitrogen")], ".raw")
d <- cbind(d, sdCheck)

3.9 Check round 1 ids

Check for unique ids in the round 1 data

length(d$Soil_Sample_Id)==length(unique(d$Soil_Sample_Id))
[1] FALSE
dups <- d$Soil_Sample_Id[duplicated(d$Soil_Sample_Id) & !is.na(d$Soil_Sample_Id)]

Let’s use the baseline data to resolve these duplicated ids

roundId <- d %>%
  dplyr::select(district, site, Soil_Sample_Id) %>%
  filter(d$Soil_Sample_Id %in% dups) %>%
  filter(!is.na(district) & !is.na(Soil_Sample_Id))

The first one is marked as both OAF and not OAF in the follow up. The surveys happened on consecutive days. I’m going to keep the obersvation with the same phone number from the baseline.

# duplicated id 1
idCheck <- d[d$Soil_Sample_Id %in% dups[1],]
bCheck <- b[b$Soil_Sample_Id %in% dups[1],]
d <- d[-which(d$Soil_Sample_Id==dups[1] & d$phone!=724903366),]

I think the second survey more closely resembles the baseline version of this farmer in the data. I’m keeping the survey done by Lazarus.

#duplicated id 2
idCheck <- d[d$Soil_Sample_Id %in% dups[2],]
bCheck <- b[b$Soil_Sample_Id %in% dups[2],]
d <- d[-which(d$Soil_Sample_Id==dups[2] & d$metadata.username!="lazarus.m"),]

The age and the GPS coordinate indicate that the survey with the age of 54 is probably the right one.

#duplicated id 3
idCheck <- d[d$Soil_Sample_Id %in% dups[3],]
bCheck <- b[b$Soil_Sample_Id %in% dups[3],]
d <- d[-which(d$Soil_Sample_Id==dups[3] & d$age!=54),]

Quick check of the work. There are still Soil_Sample_Id that are missing. Most of them have a new soil sample id but not all.

TODO - check with Charles as to why someone might not have a Soil_Sample_Id from the baseline but would have a new Soil_Sample_Id in round 1.

There are not that many new soil sample ids so perhaps it was a way to add more farmers to the sample?

length(d$Soil_Sample_Id)==length(unique(d$Soil_Sample_Id))
[1] FALSE
table(is.na(d$Soil_Sample_Id))

FALSE  TRUE 
 1872    62 
#d[is.na(d$Soil_Sample_Id),]

FOR THE TIME BEING: drop those missing a soil sample id in round 1.

d <- d[-which(is.na(d$Soil_Sample_Id)),]

3.10 Align baseline and round 1 vars

I’ll do a couple things:

  • Add season variable for eventual rbind
  • Variable names to lowercase
  • Change baseline variable names to match follow up.
  • resolve duplicates in the baseline data (where’d the come from?)

3.10.1 Baseline duplicates

length(b$Soil_Sample_Id)==length(unique(b$Soil_Sample_Id))
[1] FALSE
dups <- b$Soil_Sample_Id[duplicated(b$Soil_Sample_Id) & !is.na(b$Soil_Sample_Id)]

Resolve them here. They all appear to be control values that were used twice. Can I assign them a new control value? Probably not. See who they followed up with the first round to know which one to keep.

#b[b$soil_sample_id=="c330",] # not clear
#b[b$soil_sample_id=="c384",] # it's exactly the same, drop one
#b[b$soil_sample_id=="c1129",] # it's exactly the same, drop one
#b[b$soil_sample_id=="c626",] # not clear
#b[b$soil_sample_id=="c856",] # it's exactly the same, drop one
dropOne <- c("c384", "c1129", "c856")
b$n <- ave(1:length(b$Soil_Sample_Id), b$Soil_Sample_Id, FUN = seq_along)
b$drop <- ifelse(b$n==2 & b$Soil_Sample_Id %in% dropOne, 1, 0)
b <- b[-which(b$drop==1),]
  
dropForNow <- c("c330", "c626") # see below

TODO - confirm the data cleaning process at the baseline so that the soil data variables are being treated the same way in the baseline and round 1. They should probably be cleaned separately but should be treated the same way.

d$season <- "2016"
b$season <- "2015"
names(d) <- tolower(names(d))
names(b) <- tolower(names(b))
# convert baseline soil texture vars into one variable
b <- b %>%
  mutate(soil.texture = ifelse(clay_soil==1, "clay", 
                               ifelse(loam_soil==1, "loam",
                                      ifelse(sandy_soil==1, "sand", "unknown"))))
bUpdate <- b %>% rename(
  region = regionname,
  district = districtname,
  site = sitename,
  respondent.gender = gender, # check that this is correct
  plot.location = field_location,
  plot.size = field_size,
  harvestcomp.2016 = yield_comparative,
  main.crop = plot_information.maincrop,
  main.crop.specify = plot_information.specify_other_main_crop,
  seed.type = seedtype, 
  seed.kgs = seedkgs,
  intercrop = intercrop.intercrop,
  intercrop.specify = intercrop.specify_other_main_crop,
  intercrop.seed.type = intercrop.seedtype,
  intercrop.seed.kgs = intercrop.seedkgs,
  intercrop.harvestcomp = intercrop.yield_comparative,
  fertilizer.main = inputs.input_maincrop,
  dap.main = inputs.dap_kg,
  can.main = inputs.can_kg,
  npk.main = inputs.npk_kg,
  urea.main = inputs.urea_kg,
  fertilizer.intercrop = inputs.input_intercrop,
  dap.intercrop = inputs.dap_kg_intercrop,
  can.intercrop = inputs.can_kg_intercrop,
  npk.intercrop = inputs.npk_kg_intercrop,
  urea.intercrop = inputs.urea_kg_intercrop,
  compost.kgs = inputs.compost,
  compost.quality = inputs.compost_quality,
  compost.type = inputs.compost_type,
  lime = inputs.lime,
  lime.kgs = inputs.lime_kg,
  soil.type = soil_type,
  soil.color = soil_color,
  erosion = anti_erosion,
  field.location = location,
  seasons.oaf = seasons_oaf,
  x.c.e.c = c.e.c,
  copper = cu,
  x.ec..salts. = ec,
  #x.exchangeable.aluminum = exch.al,
  x.phosphorus.sorption.index..psi. = psi,
  potassium = k,
  magnesium = mg,
  manganese = mn,
  boron = b,
  calcium = ca,
  iron = fe,
  x.sodium = na,
  phosphorus = p, 
  sulphur = s,
  zinc = zn,
  x.organic.carbon = total.c,
  x.total.nitrogen = total.n,
  x.exchangeable.aluminium = exch.al
)
# helper to know what else needs to be changed
names(d)[!names(d) %in% names(bUpdate)]
 [1] "appformid"                             "id"                                   
 [3] "domain"                                "date_header"                          
 [5] "metadata.deviceid"                     "metadata.userid"                      
 [7] "metadata.username"                     "metadata.timestart"                   
 [9] "metadata.timeend"                      "form.case.@case_id"                   
[11] "same.hh"                               "agree"                                
[13] "interviewed.2016"                      "refusal"                              
[15] "phone"                                 "district1"                            
[17] "site1"                                 "participation"                        
[19] "hhsize"                                "which.seasons"                        
[21] "own"                                   "relation.land"                        
[23] "own.2016"                              "most.farming"                         
[25] "yield.unit"                            "intercrop.yield.unit"                 
[27] "fertility.comp"                        "fallow.yn"                            
[29] "fallow.cover"                          "residues"                             
[31] "slope"                                 "sampling.barcode"                     
[33] "comment"                               "#form/sampling2017_complete"          
[35] "new_soil_sample_id"                    "plot.m2"                              
[37] "seed.acre"                             "intercrop.seed.acre"                  
[39] "intercrop.dap.acre"                    "intercrop.can.acre"                   
[41] "intercrop.npk.acre"                    "intercrop.urea.acre"                  
[43] "yield.kg"                              "ph.raw"                               
[45] "phosphorus.raw"                        "potassium.raw"                        
[47] "calcium.raw"                           "sulphur.raw"                          
[49] "boron.raw"                             "zinc.raw"                             
[51] "x.sodium.raw"                          "x.c.e.c.raw"                          
[53] "x.ec..salts..raw"                      "magnesium.raw"                        
[55] "iron.raw"                              "x.phosphorus.sorption.index..psi..raw"
[57] "x.organic.carbon.raw"                  "manganese.raw"                        
[59] "copper.raw"                            "aluminium.raw"                        
[61] "x.exchangeable.aluminium.raw"          "x.exchangeable.acidity.raw"           
[63] "x.total.nitrogen.raw"                  "aluminium"                            
[65] "x.exchangeable.acidity"               
names(bUpdate)[!names(bUpdate) %in% names(d)]
 [1] "ssn"                        "info.formid"                "last_name"                 
 [4] "first_name"                 "clientphone"                "seasons_oaf_specify"       
 [7] "chemical_fertilizer_5"      "why_no_chemical_fertilizer" "compost_fertilizer_5"      
[10] "why_no_compost"             "lime_fertilizer_5"          "why_no_lime"               
[13] "fertilizer_5"               "legum_maincrop_5"           "legum_intercrop_5"         
[16] "crop_rese"                  "fertile_comparison"         "globalclientid"            
[19] "soil_sampling_complete"     "info.caseid"                "info.completed_time"       
[22] "info.username"              "info.started_time"          "date.sampled"              
[25] "oaf_id"                     "hp"                         "lat"                       
[28] "lon"                        "alt"                        "precision"                 
[31] "valley"                     "hilltop"                    "hillside"                  
[34] "crop.maize"                 "dap.check"                  "field_size.adj"            
[37] "seasons_breaks"             "own.cows"                   "own.goats"                 
[40] "own.chickens"               "own.pigs"                   "own.sheep"                 
[43] "clay_soil"                  "loam_soil"                  "sandy_soil"                
[46] "n"                          "drop"                      

The variables that remain to be aligned are the soil variables and any resulting calulations such as fertilizer per acre. These caculations can be redone once the data is combined.

Make groups of variables to make them easier to find later

historicalBaseline <- names(b)[which(names(b)=="chemical_fertilizer_5"):which(names(b)=="legum_intercrop_5")]
baselineSoilVars <- names(b)[which(names(b)=="c.e.c"):which(names(b)=="total.n")]

3.11 Combine baseline and round 1

d <- d %>%
  mutate(
    soil_sample_id = tolower(gsub(" ", "", soil_sample_id))
  )

commonVars <- names(d)[names(d) %in% names(bUpdate)] 

write.csv(commonVars, file="varNamesforM&E.csv")

m2ToAcres <- function(input, meters) {
  res <- (input/meters)*4046
  return(res)
}

# check soil sample id matches before rbind
table(d$soil_sample_id %in% bUpdate$soil_sample_id)
d$soil_sample_id[!d$soil_sample_id %in% bUpdate$soil_sample_id]

table(bUpdate$soil_sample_id %in% d$soil_sample_id)
bUpdate$soil_sample_id[!bUpdate$soil_sample_id %in% d$soil_sample_id]

fieldDat <- rbind(bUpdate[,commonVars], d[,commonVars])

fieldDat <- fieldDat %>% 
  mutate(
  plot.m2 = plot.size*4046,
  can.acre = m2ToAcres(can.main, plot.m2),
  dap.acre = m2ToAcres(dap.main,plot.m2),
  npk.acre = m2ToAcres(npk.main,plot.m2),
  urea.acre = m2ToAcres(urea.main,plot.m2),
  compost.acre = m2ToAcres(compost.kgs,plot.m2),
  seed.acre = m2ToAcres(seed.kgs,plot.m2),

  intercrop.seed.acre = m2ToAcres(intercrop.seed.kgs, plot.m2),
  intercrop.dap.acre = m2ToAcres(dap.intercrop, plot.m2),
  intercrop.can.acre = m2ToAcres(can.intercrop, plot.m2),
  intercrop.npk.acre = m2ToAcres(npk.intercrop, plot.m2),
  intercrop.urea.acre = m2ToAcres(urea.intercrop, plot.m2),
  soil.texture = tolower(soil.texture)
) %>% rename(
  d_client = oaf,
  sample_id = soil_sample_id
)

# drop the ids that are duplicates
fieldDat <- fieldDat[!fieldDat$sample_id %in% dropForNow,]


# and a bit more soil texture cleaning
fieldDat <- fieldDat %>%
  mutate(soil.texture = gsub("caly", "clay", soil.texture),
         soil.texture = gsub("\\.", "", soil.texture),
         soil.texture = gsub("siltay|silaty|silty ", "silty", soil.texture),
         soil.texture = gsub(" ", "", soil.texture))

3.11.1 Export data for Stefan and Step

January 9th, 2018 - fulfilling request for soil data and additional variables

fieldDat %>%
  filter(season=="2016") %>%
  dplyr::select(region, district, site, ph, x.organic.carbon, soil.texture, soil.type, soil.color, 
         x.total.nitrogen, gps, field.location, compost.kgs, compost.quality, compost.type) %>%
  write.csv(., file="cKeData_forStefanandStep.csv", row.names = F)

Check that categorical variable responses align between the baseline and round 1.

catVars <- names(fieldDat)[sapply(fieldDat, function(x){
  is.character(x)
})]
fieldNonAdmin <- catVars[!catVars %in% c("plot.location", "gps", "sample_id", "main.crop.specify", "intercrop.specify", "photo", "site")]
fieldDat$region <- ifelse(grepl("nyan", tolower(fieldDat$region)), "Nyanza", "Western")
for(i in 1:length(fieldNonAdmin)){
  repGraphs(fieldDat, fieldNonAdmin[i])
}

TODO

  • reality check the data cleaning and management process between baseline and round 1, make sure they’re the same.
  • reality check the append
  • reality check GPS

4 Analysis

The aim of the analysis is to:

TODO

  • Check movement in the sample
  • resolve duplicates in the baseline (see below!)
  • cut straight to the tables in the report ala Rwanda

4.1 Client movement

fieldDat %>%
  dplyr::select(sample_id, season, d_client) %>%
  group_by(sample_id) %>%
  spread(., season, d_client) %>%
  rename(
    client15 = `2015`,
    client16 = `2016`
  ) %>%
  mutate(
    becameClient = ifelse(client15==0 & client16==1, 1, 0),
    becameControl = ifelse(client15==1 & client16==0, 1, 0),
    stayedClient = ifelse(client15==1 & client16==1, 1, 0),
    stayedControl = ifelse(client15==0 & client16==0, 1, 0)
  ) %>% 
  ungroup() %>%
  dplyr::summarize_each(
    funs(mean= mean(., na.rm=T)), -c(sample_id, client15, client16)
  ) %>% 
  mutate_each(
    funs(paste0(round(.,2)*100, "%"))
  ) %>%
  kable(caption="Movement in Sample", format='markdown')

4.2 Client counts

clientCount <- fieldDat %>%
  dplyr::select(sample_id, season, d_client) %>%
  group_by(sample_id) %>%
  spread(., season, d_client) %>%
  rename(
    client15 = `2015`,
    client16 = `2016`
  )


clientCountTab <- cbind(
  as.data.frame(table(clientCount$client15, useNA = 'ifany')),
  as.data.frame(table(clientCount$client16, useNA = 'ifany')))

clientCountTab <- clientCountTab[,-3]
names(clientCountTab) <- c("Treatment", "Clients 2015", "Clients 2016")
write.csv(clientCountTab, file=paste0("output/", "clientCountTab.csv"), row.names = F)

4.2.1 Regressions

See sketch of SHS report.

TODO - START HERE

  • run regression
  • bring in yield values, merge soil vales, follow the other template
  • produce remaining tables

Consider including:

  • time FEs
  • age and a squared age term
  • gender (absorbed by fixed effects)
  • years of education (absorbed by fixed effects)
  • bootstrapped st. errors / robust standard errors

Helpful link for executing code in parallel

source("../oaflib/plm.R")
fieldSoilDat <- fieldDat %>%
  mutate(
    age2 = age^2
  )

keySoilVars <- c("ph", "calcium", "magnesium", "x.organic.carbon", "x.total.nitrogen")

indFeList <- list("as.factor(d_client)", 
                  c("as.factor(d_client)", "as.factor(sample_id)"),
                  c("as.factor(d_client)", "as.factor(sample_id)", "as.factor(season)"),
                  c("as.factor(d_client)", "as.factor(sample_id)", "as.factor(season)", "age", "age2"))


# run this in parallel to speed up the process
# load the data and variables and packages into the cluster
regFile <- "regFile.RData"
#forceUpdate <- forceUpdateAll
if(!file.exists(regFile) || forceUpdate) {
library(parallel)
no_cores <- detectCores() - 1

cl <- makeCluster(no_cores, type="FORK")
clusterEvalQ(cl, "plm")
clusterExport(cl, "fieldSoilDat")
clusterExport(cl, "keySoilVars")
clusterExport(cl, "indFeList")

indFeLoop <- parLapply(cl, indFeList, function(mod){
  lapply(keySoilVars, function(outcome){
    form = lm(reformulate(termlabels = mod, response = outcome), data=fieldSoilDat)
    
    pdf(file=paste("output/", paste0(outcome, paste(mod, collapse = "")), ".pdf", sep = "")) 
    print(plot(form))
    dev.off()
    
    form = plm(form, c("sample_id", "age", "age2"))
    
    rownames(form) = paste(rownames(form), outcome, sep = " ")
    return(form)
  })
  
})
stopCluster(cl)
save(indFeLoop, file=regFile)
} else {
  load("regFile.RData")
}

And combine model outputs into tables for each model

modExport <- lapply(indFeLoop, function(models){
  do.call(rbind, models)
})

for(i in 1:length(modExport)){
  write.csv(modExport[i], file=paste0("output/","regOutput_", i, ".csv"), row.names = T)
}

In the individual fixed effect model above, the naive model would only include a client indicator and individual fixed effects. If we add season, we lose significance on almost everything. I’d guess that as we add more likely controls we additionally lose signficance. I’ve included age and age squared along the lines of Hicks et.al.

finalModel <- modExport[4]

kable(finalModel, format="markdown")
write.csv(finalModel, file="output/indFe.csv")

5 Summary

LS0tCnRpdGxlOiAiS2VueWEgU0hTIFJvdW5kIDEiCmF1dGhvcjogJ1tNYXR0IExvd2VzXShtYWlsdG86bWF0dC5sb3dlc0BvbmVhY3JlZnVuZC5vcmcpJwpkYXRlOiAnYHIgZm9ybWF0KFN5cy50aW1lKCksICIlQiAlZCwgJVkiKWAnCm91dHB1dDoKICBodG1sX25vdGVib29rOgogICAgbnVtYmVyX3NlY3Rpb25zOiB5ZXMKICAgIGNvZGVfZm9sZGluZzogc2hvdwogICAgdGhlbWU6IGZsYXRseQogICAgdG9jOiB5ZXMKICAgIHRvY19kZXB0aDogNgogICAgdG9jX2Zsb2F0OiB5ZXMKLS0tCgpgYGB7ciBzZXR1cCwgaW5jbHVkZT1GQUxTRX0Ka25pdHI6Om9wdHNfY2h1bmskc2V0KGVjaG8gPSBUUlVFKQpgYGAKCiMgT2JqZWN0aXZlCgpUaGUgb2JqZWN0aXZlIG9mIHRoaXMgYW5hbHlzaXMgaXMgdG8gaWRlbnRpZnkgcG90ZW50aWFsIGRhdGEgcXVhbGl0eSBpc3N1ZXMgd2l0aCBpbiBjb21pbmcgS2VueWEgc29pbCBoZWFsdGggcm91bmQgMSBzdHVkeSBkYXRhIHNvIHRoYXQgdGhlIGVudW1lcmF0aW9uIHRlYW0gY2FuIGFkZHJlc3MgaXNzdWVzIGluIHRoZSBmaWVsZCBhbmQgaW1wcm92ZSBvdmVyYWxsIHF1YWxpdHkuIFRoaXMgZmlsZSB3aWxsIGFsc28gc2VydmUgYXMgKDEpIGFuIGluaXRpYWwgY2xlYW5pbmcgZmlsZSBhbmQgKDIpIGFuIGF0dHJpdGlvbiBhbmQgYmFsYW5jZSBjaGVjayB0byBpbmZvcm0gd2hlbiB0byB3cmFwIHVwIGVudW1lcmF0aW9uLgoKIyBLZXkgVGFrZWF3YXlzCgo+IDEpIFdlIGhhdmUgbGlrZWx5IG91dGxpZXJzIGluIHRoZSBudW1lcmljIGRhdGEuIEZvbGxvdyB1cCB3aXRoIGVudW1lcmF0b3JzIGFib3V0IHRoZXNlIHZhbHVlcy4KPiAyKSBNYW55IHJvdW5kIDEgR1BTIHBvaW50cyBhcmUgbm90IGNsb3NlIHRvIHRoZSBiYXNlbGluZSB2YWx1ZS4gRm9sbG93IHVwIGFib3V0IHBsb3QgY29udGludWl0eSBhbmQgR1BTIHF1YWxpdHkuIChzZWUgS2VueWEgcm91bmQgMSBkYXRhIGNoZWNrKQoKYGBge3IsIG1lc3NhZ2U9Rn0Kcm0obGlzdCA9IGxzKCkpCmNhdCgiXDAxNCIpCgojIyBzZXQgdXAgc29tZSBnbG9iYWwgb3B0aW9ucwojIGFsd2F5cyBzZXQgc3RyaW5nc0FzRmFjdG9ycyA9IEYgd2hlbiBsb2FkaW5nIGRhdGEKb3B0aW9ucyhzdHJpbmdzQXNGYWN0b3JzPUZBTFNFKQoKIyBzaG93IHRoZSBjb2RlCmtuaXRyOjpvcHRzX2NodW5rJHNldChlY2hvID0gVFJVRSkKCiMgZGVmaW5lIGFsbCBrbml0ciB0YWJsZXMgdG8gYmUgaHRtbCBmb3JtYXQKb3B0aW9ucyhrbml0ci50YWJsZS5mb3JtYXQgPSAnaHRtbCcpCgojIGNoYW5nZSBjb2RlIGNodW5rIGRlZmF1bHQgdG8gbm90IHNob3cgd2FybmluZ3Mgb3IgbWVzc2FnZXMKa25pdHI6Om9wdHNfY2h1bmskc2V0KHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQoKbGlicyA8LSBjKCJkcGx5ciIsICJyZXNoYXBlMiIsICJrbml0ciIsICJnZ3Bsb3QyIiwgInRpYmJsZSIsICJyZWFkeGwiLCAKICAgICJNQVNTIiwgImdyaWRFeHRyYSIsICJjb3dwbG90IiwgInJvYnVzdGJhc2UiLCAiY2FyIiwgIlJTdGF0YSIsICJmb3JlaWduIiwKICAgICJ0aWR5ciIsICJyZWFkeGwiLCAic3RyaW5nciIsICJzcCIsICJkaXNtbyIsICJsZWFmbGV0IiwgIlhNTCIsICJnZ21hcCIsICJrbml0ciIsCiAgICAiTUFTUyIpCmxhcHBseShsaWJzLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFQsIHF1aWV0bHkgPSBULCB3YXJuLmNvbmZsaWN0cyA9IEYpCgojIyMjIGRlZmluZSBoZWxwZnVsIGZ1bmN0aW9ucwojIGRlZmluZSBmdW5jdGlvbiB0byBhZGp1c3QgdGFibGUgd2lkdGhzCmh0bWxfdGFibGVfd2lkdGggPC0gZnVuY3Rpb24oa2FibGVfb3V0cHV0LCB3aWR0aCkgewogIHdpZHRoX2h0bWwgPC0gcGFzdGUwKHBhc3RlMCgnPGNvbCB3aWR0aD0iJywgd2lkdGgsICciPicpLCBjb2xsYXBzZSA9ICJcbiIpCiAgc3ViKCI8dGFibGU+IiwgcGFzdGUwKCI8dGFibGU+XG4iLCB3aWR0aF9odG1sKSwga2FibGVfb3V0cHV0KQp9Cgpzb3VyY2UoIi4uL29hZmxpYi9jb21tY2FyZUV4cG9ydC5SIikKc291cmNlKCIuLi9vYWZsaWIvbWlzYy5SIikKYGBgCgojIERhdGEKCiMjIEltcG9ydCByb3VuZCAxIGRhdGEKCkknbSBnb2luZyB0byB1c2UgdGhlIENvbW1DYXJlIEFQSSBjcmVhdGVkIGJ5IFtSb2JlcnQgT25dKHJvYmVydC5vbkBvbmVhY3JlZnVuZC5vcmcpIHRvIGFjY2VzcyB0aGUgZGF0YSBkaXJlY3RseSBmcm9tIENvbW1DYXJlLiBUaGlzIGVuc3VyZXMgdGhhdCB0aGVyZSBhcmUgbm8gY2hhbmdlcyB0byB0aGUgZGF0YSBiZXR3ZWVuIHRoZSBwb2ludCBvZiBhY2Nlc3MgYW5kIHRoZSBwb2ludCBvZiBjbGVhbmluZyBhbmQgYW5hbHlzaXMKCmBgYHtyfQpmb3JjZVVwZGF0ZUFsbCA9IEYKZm9yY2VVcGRhdGUgPSBmb3JjZVVwZGF0ZUFsbApkIDwtIGdldEZvcm1EYXRhKCJoYXJ2ZXN0IiwgIlNvaWwgU2FtcGxpbmciLCAiU29pbCBTYW1wbGluZyAyMDE3IiwgZm9yY2VVcGRhdGUpCiMgYWRkIGluIGZvcmNlIHVwZGF0ZSB0byBnZXQgbGF0ZXN0IHJlcG9ydApgYGAKCiMjIEltcG9ydCBiYXNlbGluZQoKYGBge3J9CmJhc2VsaW5lRGlyIDwtIG5vcm1hbGl6ZVBhdGgoZmlsZS5wYXRoKCIuLiIsICJrZV9zaHNfYmFzZWxpbmUiLCAiZGF0YSIpKQoKYiA8LSByZWFkLmNzdihmaWxlPXBhc3RlMChiYXNlbGluZURpciwgIi9zaHMga2UgYmFzZWxpbmUuY3N2IikpICMgb2JqIGQKYGBgCgojIyBJbXBvcnQgc29pbCBkYXRhCgpIZXJlIEkgYWNjZXNzIHRoZSBzb2lsIHByZWRpY3Rpb25zIGZyb20gdGhlIE9BRiBzb2lsIGxhYi4gW1BhdHJpY2sgQmVsbF0obWFpbHRvOnBhdHJpY2suYmVsbEBvbmVhY3JlZnVuZC5vcmcpIG1hbmFnZXMgdGhlIGxhYiBhbmQgW01pa2UgQmFyYmVyXShtaWtlLmJhcmJlckBvbmVhY3JlZnVuZC5vcmcpIG92ZXJzZWVzIHRoZSBwcmVkaWN0aW9uIHNjcmlwdHMuCgpgYGB7cn0Kc29pbERpciA8LSBub3JtYWxpemVQYXRoKGZpbGUucGF0aCgiLi4iLCAiLi4iLCAiZGF0YSIsICJPQUYgU29pbCBMYWIgRm9sZGVyIiwgIlByb2plY3RzIiwgImtlX3Noc18xNyIsICI0X3ByZWRpY3RlZCIsICJvdGhlcl9zdW1tYXJpZXMiKSkKc29pbCA8LSByZWFkLmNzdihmaWxlPXBhc3RlKHNvaWxEaXIsICJjb21iaW5lZC1wcmVkaWN0aW9ucy1pbmNsdWRpbmctYmFkLW9uZXMuY3N2Iiwgc2VwID0gIi8iKSkKCmlkRGlyIDwtIG5vcm1hbGl6ZVBhdGgoZmlsZS5wYXRoKCIuLiIsICIuLiIsICJkYXRhIiwgIk9BRiBTb2lsIExhYiBGb2xkZXIiLCAiUHJvamVjdHMiLCAia2Vfc2hzXzE3IiwgIjVfbWVyZ2VkIikpCklkZW50aWZpZXJzIDwtIHJlYWRfZXhjZWwocGFzdGUoaWREaXIsIlNvaWwgU2FtcGxpbmcgLSBTYW1wbGUgcmVjZXB0aW9uIDIwMTcgLSBTYW1wbGUgcmVjZXB0aW9uIDIwMTcyMDE3LTA0LTEyICgxKS54bHN4IixzZXA9Ii8iKSwgc2hlZXQ9MSkgJT4lCiAgbXV0YXRlKAogICAgcmVjZXB0aW9uX2JhcmNvZGUgPSBnc3ViKCJfIiwgIiIsIHJlY2VwdGlvbl9iYXJjb2RlKQogICkKCnNvaWwkU1NOWyFzb2lsJFNTTiAlaW4lIElkZW50aWZpZXJzJHJlY2VwdGlvbl9iYXJjb2RlXQpJZGVudGlmaWVycyRyZWNlcHRpb25fYmFyY29kZVshSWRlbnRpZmllcnMkcmVjZXB0aW9uX2JhcmNvZGUgJWluJSBzb2lsJFNTTl0KYGBgCgpJdCBzZWVtcyB0byBiZSB0aGUgY2FzZSB0aGF0IG9uZSBiYXJjb2RlIGlzIG5vdCBtYXRjaGluZyBkdWUgdG8gdGhlIG51bWJlciBvZiB6ZXJvcy4gQ29ycmVjdCBhbmQgdXBkYXRlLgpgYGB7cn0Kc29pbCRTU05bc29pbCRTU049PSJvYWZrZTAwMjEyNiJdIDwtICJvYWZrZTAwMDIxMjYiCmBgYAoKVGhlcmUncyBvbmUgcmVjb3JkIGluIHRoZSBJZGVudGlmaWVycyB0aGF0IGRvZXNuJ3QgYXBwZWFyIGluIHRoZSBkYXRhLiBBbiBleHRyYSBzYW1wbGUgd2l0aG91dCBhIHN1cnZleT8gU2VlbXMgb2RkLgoKIyMgVXBkYXRlIHZhcmlhYmxlIG5hbWVzCgpUaGUgdmFyaWFibGUgbmFtZXMgYXJlIG91dCBvZiBjb250cm9sLiBJbiBvcmRlciB0byBtYWtlIHRoZSB2YXJpYWJsZSBuYW1lcyBtb3JlIHVzZXIgZnJpZW5kbHksIEkgd2VudCB0byBbQ29tbUNhcmVdKHd3dy5jb21tY2FyZWhxLmNvbSkgYW5kIGRvd25sb2FkZWQgdGhlIGZvcm0gY29udGVudHMgdGhhdCBpbmNsdWRlcyB0aGUgdmFyaWFibGUgbmFtZS4gSSB0aGVuIHdyb3RlIGluIGEgbW9yZSB1c2VyIGZyaWVuZGx5IHZhcmlhYmxlIG5hbWUgaW4gdGhlIGZpbGUuIEkgaW1wb3J0IGFuZCBvdmVyd3JpdGUgdGhlIHZhcmlhYmxlIG5hbWVzIGhlcmUuIFBlcmhhcHMgdGhlcmUncyBhIGJldHRlciB3YXkgdG8gZG8gdGhpcyBidXQgaXQncyBlYXNpZXIgdG8gbG9nIHRoZSBjaGFuZ2VzIGluIGV4Y2VsIHRoYW4gdG8gd3JpdGUgb3V0IGFsbCB0aGUgY29kZS4gVGhlIHJlZmVyZW5jZSBmaWxlcyB3aWxsIGJlIG9uIGhhbmQgaW4gY2FzZSBJIGFjY2lkZW50YWxseSBtaXNsYWJlbCBhIHZhcmlhYmxlLiBUaGUgdmFyaWFibGUgbmFtZXMgaW4gdGhlIGV4Y2VsIGZpbGUgc3RhcnQgd2l0aCB2YXJpYWJsZSAxMSBmcm9tIENvbW1DYXJlLiBUaGUgZmlyc3QgMTAgQ29tbUNhcmUgdmFyaWFibGVzIGFyZSBtZXRhIGRhdGEgd2hpY2ggd2Ugd2lsbCBrZWVwLgoKR2VuZXJhbCBUT0RPCgoqIGltcG9ydCByb3VuZCAxIHNvaWwgZGF0YQoqIGRvIHNvbWUgY2xlYW5pbmcgYW5kIGNoZWNraW5nIG9mIHJvdW5kIDEgZGF0YQoqIGltcG9ydCBiYXNlbGluZSBkYXRhCiogYWxpZ24gYmFzZWxpbmUgYW5kIHJvdW5kIDEgZGF0YSBsb25nIAoKYGBge3IsIG1lc3NhZ2VzPUZ9Cm5ld05hbWUgPC0gcmVhZF9leGNlbCgidmFyX25hbWVzLnhsc3giLCBzaGVldD0xKQpgYGAKCmBgYHtyfQpxVHlwZXMgPC0gYygiTXVsdGlwbGUgQ2hvaWNlIiwgIlBob25lIE51bWJlciBvciBOdW1lcmljIElEIiwgIkNoZWNrYm94IiwgIlRleHQiLCAiRGVjaW1hbCIsICJJbWFnZSBDYXB0dXJlIiwgIkJhcmNvZGUgU2NhbiIsICJHUFMiLCAiSW50ZWdlciIpCgpuZXdOYW1lIDwtIG5ld05hbWUgJT4lIGRwbHlyOjpzZWxlY3QoMTo0CikgJT4lIGRwbHlyOjpmaWx0ZXIobmV3TmFtZSRUeXBlICVpbiUgcVR5cGVzKSAlPiUgYXMuZGF0YS5mcmFtZSgpCm5ld05hbWUgPC0gbmV3TmFtZSAlPiUgZmlsdGVyKG5ldy52YXIubmFtZSE9ImdlbmVyYWwuY29tbWVudCIpCmBgYAoKS2VlcCB0aGUgcGFydGljaXBhdGlvbiB2YXJpYWJsZSBpbiBgZGAgc28gd2UgdW5kZXJzdGFuZCB3aG8gYWdyZWVkIHRvIHBhcnRpY2lwYXRlLiBNYWtlIGFkZGl0aW9uYWwgY2hhbmdlcyB0byBgZGAgdG8gcHJlcGFyZSBpdCB0byBiZSBjb21iaW5lZCB3aXRoIHRoZSBtZXRhIGluZm9ybWF0aW9uIGZyb20gQ29tbUNhcmUuCgpgYGB7cn0KbmFtZXMoZClbMjZdIDwtICJwYXJ0aWNpcGF0aW9uIgpuYW1lcyhkKVtuYW1lcyhkKT09IiNmb3JtL2lkX2Jhc2UvU29pbF9TYW1wbGVfSWQiXSA8LSAiU29pbF9TYW1wbGVfSWQiCm5hbWVzKGQpW25hbWVzKGQpPT0iI2Zvcm0vaWRfMjAxN25ldy9Tb2lsX1NhbXBsZV9JZCJdIDwtICJOZXdfc29pbF9zYW1wbGVfaWQiCmBgYAoKTWVyZ2UgaW4gdmFyaWFibGUgbmFtZXMgdG8gY2hlY2sgdGhhdCB0aGV5IG1hdGNoIGFuZCB0aGVuIHJlcGxhY2UgYG5hbWVzKClgLiBJJ20gc3VidHJhY3RpbmcgdHdvIGJlY2F1c2UgdGhlIGBTb2lsX1NhbXBsZV9JZGAgdmFyaWFibGVzIGFwcGVhciBvdXQgb2Ygb3JkZXIuCgpJJ20gYWxzbyBhZGRpbmcgc29tZSB2YXJpYWJsZXMgdG8gdGhlIG5ldyBuYW1lIHZlY3RvciBhcyBkIG5vdyBoYXMgYSBkaWZmZXJlbnQgdmFyaWFibGUgbGVuZ3RoCgpgYGB7cn0KbmV3TmFtZVZlYyA8LSBjKG5ld05hbWUkbmV3LnZhci5uYW1lWzFdLCAiYWdyZWUiLCAiaW50ZXJ2aWV3ZWQuMjAxNiIsICJyZWZ1c2FsIiwgbmV3TmFtZSRuZXcudmFyLm5hbWVbMjoobGVuZ3RoKG5ld05hbWUkbmV3LnZhci5uYW1lKS0xKV0sICJjb21tZW50IiwgbmV3TmFtZSRuZXcudmFyLm5hbWVbbGVuZ3RoKG5ld05hbWUkbmV3LnZhci5uYW1lKV0pCgpuYW1lcyhkKVsxMToobGVuZ3RoKGQpLTMpXSA8LSBuZXdOYW1lVmVjCgojIGRyb3AgdmFycyB3aXRoIGRyb3AKZCA8LSBkWywtd2hpY2goZ3JlcGwoImRyb3AuIiwgbmFtZXMoZCkpKV0KYGBgCgpNYWtlIG5ldyB2YXJpYWJsZXMKCmBgYHtyfQojY29udmVydCBhY3JlYWdlIHRvIG0yIGFuZCB0aGVuIGNhbGN1bGF0ZSBmZXJ0aWxpemVyIHBlciBhY3JlCmQkcGxvdC5tMiA8LSBkJHBsb3Quc2l6ZSo0MDQ2CgptMlRvQWNyZXMgPC0gZnVuY3Rpb24oaW5wdXQsIG1ldGVycykgewogIHJlcyA8LSAoaW5wdXQvbWV0ZXJzKSo0MDQ2CiAgcmV0dXJuKHJlcykKfQoKZCRjYW4uYWNyZSA8LSBtMlRvQWNyZXMoZCRjYW4ubWFpbiwgZCRwbG90Lm0yKQpkJGRhcC5hY3JlIDwtIG0yVG9BY3JlcyhkJGRhcC5tYWluLGQkcGxvdC5tMikKZCRucGsuYWNyZSA8LSBtMlRvQWNyZXMoZCRucGsubWFpbixkJHBsb3QubTIpCmQkdXJlYS5hY3JlIDwtIG0yVG9BY3JlcyhkJHVyZWEubWFpbixkJHBsb3QubTIpCmQkY29tcG9zdC5hY3JlIDwtIG0yVG9BY3JlcyhkJGNvbXBvc3Qua2dzLGQkcGxvdC5tMikKZCRzZWVkLmFjcmUgPC0gbTJUb0FjcmVzKGQkc2VlZC5rZ3MsZCRwbG90Lm0yKQoKZCRpbnRlcmNyb3Auc2VlZC5hY3JlIDwtIG0yVG9BY3JlcyhkJGludGVyY3JvcC5zZWVkLmtncywgZCRwbG90Lm0yKQpkJGludGVyY3JvcC5kYXAuYWNyZSA8LSBtMlRvQWNyZXMoZCRkYXAuaW50ZXJjcm9wLCBkJHBsb3QubTIpCmQkaW50ZXJjcm9wLmNhbi5hY3JlIDwtIG0yVG9BY3JlcyhkJGNhbi5pbnRlcmNyb3AsIGQkcGxvdC5tMikKZCRpbnRlcmNyb3AubnBrLmFjcmUgPC0gbTJUb0FjcmVzKGQkbnBrLmludGVyY3JvcCwgZCRwbG90Lm0yKQpkJGludGVyY3JvcC51cmVhLmFjcmUgPC0gbTJUb0FjcmVzKGQkdXJlYS5pbnRlcmNyb3AsIGQkcGxvdC5tMikKCmBgYAoKWWllbGQgY2FsY3VsYXRpb24gLSBmaXJzdCBsZXQncyBjb25maXJtIHRoYXQgd2hlbiB0aGUgYHlpZWxkLnVuaXRgIGlzIE5BIHRoYXQgdGhlcmUgaXNuJ3QgYSB5aWVsZCB2YWx1ZS4KCmBgYHtyfQp0YWJsZShkJHlpZWxkW2lzLm5hKGQkeWllbGQudW5pdCldLCB1c2VOQSA9ICdpZmFueScpCmBgYAoKVE9ETyAtICoqQ2hhcmxlcyoqOiBJIGFzc3VtZSBhIDAgeWllbGQgaXMgYSB0cnVlIHZhbHVlLiBPciBzaG91bGQgaXQgYmUgTkE/IFRoZSBjb2RlIGJlbG93IGNvbnZlcnRzIGFsbCB5aWVsZCB2YWx1ZXMgYXNzb2NpYXRlZCB3aXRoIGFuIE5BIHVuaXQgdG8gTkEgd2hlbiBpbiBmYWN0IHRoZXJlIHNob3VsZCBiZSBzb21lIDBzLgoKYGBge3J9CmQkeWllbGQua2cgPC0gaWZlbHNlKGQkeWllbGQudW5pdD09IjEwMGtnIiwgZCR5aWVsZCoxMDAsIAogICAgICAgICAgICAgIGlmZWxzZShkJHlpZWxkLnVuaXQ9PSI1MGtnIiwgZCR5aWVsZCo1MCwKICAgICAgICAgICAgICBpZmVsc2UoZCR5aWVsZC51bml0PT0iOTBrZyIsIGQkeWllbGQqOTAsCiAgICAgICAgICAgICAgaWZlbHNlKGQkeWllbGQudW5pdD09IkdHIiwgZCR5aWVsZCoyLCBOQSkpKSkKYGBgCgojIyMgSW1wb3J0IGZlZWRiYWNrCgpIZXJlIEkgdGFrZSB0aGUgZm9sbG93IHVwIHJlc3BvbnNlcyBmcm9tIGVudW1lcmF0b3JzIGFuZCB1cGRhdGUgdGhlIHZhbHVlcyBpbiB0aGUgdGhlIGZ1bGwgZGF0YS4gSSBvbmx5IHdhbnQgdGhlIG91dGxpZXIgY2hlY2tzIHRvIHNob3cgdmFsdWVzIHRoYXQgd2UgaGF2ZW4ndCBhbHJlYWR5IGFkZHJlc3NlZCB0aHJvdWdoIHNvbGljaXRpbmcgYWRkaXRpb25hbCBmZWVkYmFjayBmcm9tIGVudW1lcmF0b3JzIGFuZCBmYXJtZXJzLiBUaGlzIGNvZGUgYXNzdW1lcyB0aGUgZGF0YSBjb21lcyBpbiB3aXRoIHJvd3MgYXMgZmFybWVycyBhbmQgY29sdW1ucyBhcyB0aGUgcXVlc3Rpb25zIHRoYXQgbmVlZGVkIHRvIGJlIGFkZHJlc3NlZC4gSSd2ZSByZXNoYXBlZCBpdCB0byBpbmNsdWRlIAoKVE9ETyAtIGNvbWUgYmFjayBhbmQgZmlndXJlIG91dCBob3cgdG8gcXVpY2tseSBhZGQgaW4gdGhlIHBob25lIGRhdGEKCmBgYHtyfQojdXBkYXRlRGlyIDwtIHBhc3RlKGdldHdkKCksICJlbnVtX3N1bW1hcmllcyIsIHNlcCA9ICIvIikKIyB1cGRhdGVzIDwtIHJlYWQuY3N2KCJQaG9uZSBjYWxscyBzdXJ2ZXkuY3N2IiwgaGVhZGVyPVQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSAlPiUgcmVuYW1lKAojICAgU29pbF9TYW1wbGVfSWQgPSBzb2lsX3NhbXBsZV9pZCwKIyAgIGFnZSA9IEFnZS5vZi5yZXNwb25kZW50LAojICAgY2FuLmludGVyY3JvcCA9IEhvdy5tYW55LktHcy5vZi5DQU4uZGlkLnlvdS5hcHBseS50by50aGUuLmludGVyY3JvcC4sCiMgICBjYW4ubWFpbiA9IEhvdy5tYW55LktHcy5vZi5DQU4uZGlkLnlvdS5hcHBseS50by50aGUubWFpbi5jcm9wLiwKIyAgIGNoaWNrZW5zID0gTnVtYmVyLm9mLmNoaWNrZW5zLkhvdy5tYW55LmNoaWNrZW5zLmRvLnlvdS5vd24uLmNoaWNrZW5zLAojICAgZGFwLmludGVyY3JvcCA9IEhvdy5tYW55LktHcy5vZi5EQVAuZGlkLnlvdS5hcHBseS50by50aGUuaW50ZXJjcm9wLiwKIyAgIGRhcC5tYWluID0gSG93Lm1hbnkuS0dzLm9mLkRBUC5kaWQueW91LmFwcGx5LnRvLnRoZS5tYWluLmNyb3AuLAojICAgaGhzaXplID0gTnVtYmVyLm9mLnBlb3BsZS5pbi50aGlzLkhvdXNlaG9sZC5JbmNsdWRlLnJlc3BvbmRlbnQuLiwKIyAgIGludGVyY3JvcC5zZWVkLmtncyA9IEhvdy5tYW55Lmtncy5vZi5zZWVkLmRpZC55b3UudXNlLmluLnlvdXIuaW50ZXJjcm9wLiwKIyAgIGxpbWUua2dzID0gSG93Lm1hbnkuS0dzLm9mLmxpbWUuZGlkLnlvdS51c2Uub24udGhpcy5wbG90LiwKIyAgIG5way5tYWluID0gSG93Lm1hbnkuS0dzLm9mLk5QSy5kaWQueW91LmFwcGx5LnRvLnRoZS5tYWluY3JvcC4sCiMgICBwbG90LnNpemUgPSBXaGF0LmlzLnlvdXIuYXByb3hpbWF0ZS5wbG90c2l6ZSwKIyAgIHNlZWQua2dzID0gSG93Lm1hbnkua2dzLm9mLnNlZWQuZGlkLnlvdS51c2UuaW4udGhlLk1haW5jcm9wLiwKIyAgIHNoZWVwID0gSG93Lm1hbnkuc2hlZXAuZG8ueW91Lm93bi4sCiMgICB1cmVhLm1haW4gPSBIb3cubWFueS5LR3Mub2YudXJlYS5kaWQueW91LmFwcGx5LnRvLnRoZS5tYWluY3JvcC4sCiMgICB5aWVsZCA9IFdoYXQud2FzLnRoZS55aWVsZC5mb3IudGhpcy5wbG90Lm1haW4uY3JvcC4KIyApICU+JSBtdXRhdGUoCiMgICB5aWVsZCA9IGlmZWxzZSh5aWVsZD09Ik4vQSIsIE5BLCAgaWZlbHNlKGdyZXBsKCJOb3QiLCB5aWVsZCksIE5BLCB5aWVsZCkpLAojICAgeWllbGQgPSBpZmVsc2UoeWllbGQ9PSIzMiBiYWdzIG9mIDkwIGtncyIsICIzMiBiYWdzIiwgeWllbGQpCiMgKQojIAojIHVwZGF0ZXMkeWllbGQudW5pdCA8LSBzYXBwbHkodXBkYXRlcyR5aWVsZCwgZnVuY3Rpb24oeCl7ICMgc2VwYXJhdGUgb3V0IHRoZSB1bml0CiMgICBzdHJzcGxpdCh4LCAiICIpW1sxXV1bMl19KQojIAojIHVwZGF0ZXMkeWllbGQua2cgPC0gc2FwcGx5KHVwZGF0ZXMkeWllbGQsIGZ1bmN0aW9uKHgpeyAjIGtlZXAgb25seSB0aGUgbnVtYmVyCiMgICBzdHJzcGxpdCh4LCAiICIpW1sxXV1bMV19KQojIAojIHVwZGF0ZXMkeWllbGQua2cgPC0gaWZlbHNlKGdyZXBsKCJiYWciLCAgdXBkYXRlcyR5aWVsZC51bml0KSwgYXMubnVtZXJpYyh1cGRhdGVzJHlpZWxkKSo5MCwgaWZlbHNlKGdyZXBsKCJnb3JvIiwgdXBkYXRlcyR5aWVsZC51bml0KSwgIGFzLm51bWVyaWModXBkYXRlcyR5aWVsZCkqMi40NSwgdXBkYXRlcyR5aWVsZCkpCiMgCiMgCiMgdXBkYXRlcyA8LSBtZWx0KHVwZGF0ZXMsIGlkLnZhcnMgPSBjKCJTb2lsX1NhbXBsZV9JZCIsICJDb21tZW50cyIpLCBtZWFzdXJlLnZhcnMgPSBuYW1lcyh1cGRhdGVzKVs2OjE5XSkKIyB1cGRhdGVzIDwtIHVwZGF0ZXMgJT4lIGZpbHRlcighaXMubmEodmFsdWUpIHwgdmFsdWUhPSAiICIpCgojIGNoZWNrIHRoYXQgdGhpcyB3b3JrczoKI3ByaW50KHVwZGF0ZXNbMSxdKQpgYGAKCiMjIE1lcmdlIHNvaWwgaW50byBkYXRhCgpUT0RPIC0gY2hlY2sgaW50byBob3cgbWFueSBtZXJnZXMgd2UncmUgbm90IGdldHRpbmcuIEZpbmFsaXplIG1lcmdlIHJlcG9ydC4KCmBgYHtyfQpkJHNhbXBsaW5nLmJhcmNvZGUgPC0gZ3N1YigiXyIsICIiLCBkJHNhbXBsaW5nLmJhcmNvZGUpCm5hbWVzKHNvaWwpW25hbWVzKHNvaWwpPT0iU1NOIl0gPC0gInNhbXBsaW5nLmJhcmNvZGUiCgpkIDwtIGxlZnRfam9pbihkLCBzb2lsLCBieT0ic2FtcGxpbmcuYmFyY29kZSIpCgpzb2lsVmFycyA8LSBuYW1lcyhzb2lsKVsyOjIxXQpgYGAKCiMjIEltcG9ydCBzb2lsIHRleHR1cmUgZGF0YQoKSSBndWVzcyBJIGRpZG4ndCBkbyB0aGF0IGVhcmxpZXIgc28gaW1wb3J0IHRoZSBzb2lsIHRleHR1cmUgZGF0YSBub3cuCgpgYGB7cn0KdGV4dCA8LSByZWFkX3hsc3goInNvaWxfdGV4dHVyZS54bHN4IikgJT4lCiAgbXV0YXRlKHJlY2VwdGlvbl9iYXJjb2RlID0gZ3N1YigiXyIsICIiLCByZWNlcHRpb25fYmFyY29kZSkpICU+JQogIHJlbmFtZShzYW1wbGluZy5iYXJjb2RlID0gcmVjZXB0aW9uX2JhcmNvZGUsCiAgICAgICAgIHNvaWwudGV4dHVyZSA9IGBzb2lsIHRleHR1cmVgKQoKZCA8LSBkICU+JQogIGxlZnRfam9pbiguLCB0ZXh0WyxjKCJzYW1wbGluZy5iYXJjb2RlIiwgInNvaWwudGV4dHVyZSIpXSwgYnkgPSAic2FtcGxpbmcuYmFyY29kZSIpCmBgYAoKIyMgT3V0bGllciBjaGVjawoKQ2hlY2sgYWxsIG51bWVyaWMgdmFyaWFibGVzIGZvciBvdXRsaWVycy4gRmlyc3QsIGl0J3MgcHJvYmFibHkgc2FmZSB0byByZXBsYWNlIGFsbCAtOTlzIHdpdGggTkEKClRPRE8gLSBzZXQgdXAgb3V0bGllciBjaGVja3MKCmBgYHtyfQpjYXRWYXJzIDwtIG5hbWVzKGQpW3NhcHBseShkLCBmdW5jdGlvbih4KXsKICBpcy5jaGFyYWN0ZXIoeCkKfSldCgplbnVtQ2xlYW4gPC0gZnVuY3Rpb24oZGF0LCB4LCB0b1JlbW92ZSl7CiAgZGF0Wyx4XSA8LSBpZmVsc2UoZGF0Wyx4XSAlaW4lIHRvUmVtb3ZlLCBOQSwgZGF0Wyx4XSkKICByZXR1cm4oZGF0Wyx4XSkKfQoKc3RyVGFibGUgPC0gZnVuY3Rpb24oZGF0LCB4KXsKICB2YXJOYW1lID0geAogIHRhYiA9IGFzLmRhdGEuZnJhbWUodGFibGUoZGF0Wyx4XSwgdXNlTkEgPSAnaWZhbnknKSkKICB0YWIgPSB0YWJbb3JkZXIodGFiJEZyZXEsIGRlY3JlYXNpbmcgPSBUKSxdCiAgZW5kID0gaWZlbHNlKGxlbmd0aCh0YWIkVmFyMSk8MTAsIGxlbmd0aCh0YWIkVmFyMSksIDEwKQogIHJlcE9yZGVyID0gcGFzdGUodGFiJFZhcjFbMTplbmRdLCBjb2xsYXBzZT0iLCAiKQogIG91dCA9IGRhdGEuZnJhbWUodmFyaWFibGUgPSB2YXJOYW1lLAogICAgICAgICAgICAgICAgICAgcmVzcG9uc2VzID0gcmVwT3JkZXIpCiAgCiAgcmV0dXJuKG91dCkKfQoKIyBjbGVhbiB1cCBrbm93biB2YWx1ZXMKY2F0RW51bVZhbHMgPC0gYygiLTk5IiwgIi04OCIsICItIDk5IiwgIi05OS4wIiwgIjg4IiwgIl84OCIsICItIDg4IiwgIjAuODgiLAogICAgICAgICAgICAgICAgICItLTg4IiwgIl9fODgiLCAiLTg4LjAiLCAiOTkuMCIpCmRbLGNhdFZhcnNdIDwtIHNhcHBseShjYXRWYXJzLCBmdW5jdGlvbih5KXsKICBkWyx5XSA8LSBlbnVtQ2xlYW4oZCx5LCBjYXRFbnVtVmFscykKfSkKCgpyZXNwb25zZVRhYmxlIDwtIGRvLmNhbGwocmJpbmQsIGxhcHBseShjYXRWYXJzLCBmdW5jdGlvbih4KXsKICBzdHJUYWJsZShkLCB4KQp9KSkKCmBgYAoKIyMjIENhdGVnb3JpY2FsIHJlc3BvbnNlIHRhYmxlCgpBIHNpbXBsZSB0YWJsZSB0byBwcmV2aWV3IHRoZSB2YWx1ZXMgaW4gdGhlIGRhdGEuIFRoZSB2YWx1ZXMgYXJlIHJhbmtlZCBieSBmcmVxdWVuY3kuCgpgYGB7cn0Ka2FibGUocmVzcG9uc2VUYWJsZSwgZm9ybWF0PSJtYXJrZG93biIpCmBgYAoKIyMjIENhdGVnb3JpY2FsIHJlc3BvbnNlIGdyYXBocwpgYGB7cn0KcmVwR3JhcGhzIDwtIGZ1bmN0aW9uKGRhdCwgeCl7CiAgdGFiID0gYXMuZGF0YS5mcmFtZSh0YWJsZShkYXRbLHhdLCB1c2VOQSA9ICdpZmFueScpKQogIHRhYiA9IHRhYltvcmRlcih0YWIkRnJlcSwgZGVjcmVhc2luZyA9IFQpLF0KICBwcmludCgKICAgIGdncGxvdChkYXRhPXRhYiwgYWVzKHg9VmFyMSwgeT1GcmVxKSkgKyBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIpICsKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIsIGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gNDUsIGhqdXN0ID0gMSkpICsKICAgICAgbGFicyh0aXRsZSA9cGFzdGUwKCJDb21wb3NpdGlvbiBvZiB2YXJpYWJsZTogIiwgeCkpCiAgKQp9CgphZG1pblZhcnMgPC0gYyhuYW1lcyhkKVtncmVwKCJtZXRhIiwgbmFtZXMoZCkpXSwiZW51bV9uYW1lIiwgInBob3RvIiwgICJwYXJ0aWNpcGF0aW9uIiwgInJlZnVzYWwiLCAicGhvbmUiLCAgImNvbW1lbnQiLCAiZ3BzIiwgIlNvaWxfU2FtcGxlX0lkIiwgInNhbXBsaW5nLmJhcmNvZGUiLCAiaWQiLCAiZG9tYWluIiwgImRhdGVfaGVhZGVyIiwgImZvcm0uY2FzZS5AY2FzZV9pZCIsICJzaXRlIiwgImRpc3RyaWN0MSIsICJzaXRlMSIsICJwbG90LmxvY2F0aW9uIiwgIk5ld19zb2lsX3NhbXBsZV9pZCIsICIjZm9ybS9TYW1wbGluZzIwMTdfQ29tcGxldGUiLCAiQXBwRm9ybUlkIikKbm9uQWRtaW5WYXJzIDwtIGNhdFZhcnNbIWNhdFZhcnMgJWluJSBhZG1pblZhcnNdCgpmb3IoaSBpbiAxOmxlbmd0aChub25BZG1pblZhcnMpKXsKICByZXBHcmFwaHMoZCwgbm9uQWRtaW5WYXJzW2ldKQp9CmBgYAoKIyMgTnVtZXJpYyB2YXJpYWJsZXMKCmBgYHtyfQpudW1WYXJzIDwtIG5hbWVzKGQpW3NhcHBseShkLCBmdW5jdGlvbih4KXsKICBpcy5udW1lcmljKHgpCn0pXQpgYGAKCkJhc2ljIGNsZWFuaW5nIG9mIGtub3duIGlzc3VlcyBsaWtlIGVudW1lcmF0b3IgY29kZXMgZm9yIERLLCBOV1IsIGV0Yy4KYGBge3J9CmVudW1WYWxzIDwtIGMoLTg4LC04NSwgLTk5KQoKZFssbnVtVmFyc10gPC0gc2FwcGx5KG51bVZhcnMsIGZ1bmN0aW9uKHkpewogIGRbLHldIDwtIGVudW1DbGVhbihkLHksIGVudW1WYWxzKQp9KQpgYGAKCiMjIyBOdW1lcmljIG91dGxpZXIgdGFibGUKCmBgYHtyfQppcXIuY2hlY2sgPC0gZnVuY3Rpb24oZGF0LCB4KSB7IAogIHExID0gc3VtbWFyeShkYXRbLHhdKVtbMl1dCiAgcTMgPSBzdW1tYXJ5KGRhdFsseF0pW1s1XV0gCiAgaXFyID0gcTMtcTEKICBtYXJrICA9IGlmZWxzZShkYXRbLHhdIDwgKHExIC0gKDEuNSppcXIpKSB8IGRhdFsseF0gPiAocTMgKyAoMS41KmlxcikpLCAxLDApCiAgdGFiID0gcmJpbmQoCiAgICBzdW1tYXJ5KGRhdFsseF0pLAogICAgc3VtbWFyeShkYXRbbWFyaz09MCwgeF0pCiAgKQogIHJldHVybih0YWIpCn0KCiMgcmVtb3ZlIGFkbWluIHZhcnMKbnVtQWRtaW5WYXJzIDwtIGMoIm1ldGFkYXRhLmRldmljZUlEIiwgIm9hZmlkIiwgIlgiKQpudW1WYXJzTm90QWRtaW4gPC0gbnVtVmFyc1shbnVtVmFycyAlaW4lIG51bUFkbWluVmFyc10KCmlxclRhYiA8LSBkby5jYWxsKHBseXI6OnJiaW5kLmZpbGwsIGxhcHBseShudW1WYXJzTm90QWRtaW4sIGZ1bmN0aW9uKHkpewogICNwcmludCh5KQogIHJlcyA9IGlxci5jaGVjayhkLCB5KQogICNwcmludChkaW0ocmVzKSkKICBvdXQgPSBkYXRhLmZyYW1lKHZhcj1yYmluZCh5LCBwYXN0ZSh5LCAiLmlxciIsIHNlcD0iIikpLCByZXMpCiAgcmV0dXJuKG91dCkKfSkpCgppcXJUYWJbLDI6OF0gPC0gc2FwcGx5KGlxclRhYlssMjo4XSwgZnVuY3Rpb24oeCl7cm91bmQoeCwxKX0pCmBgYAoKVGhlIG91dGxpZXIgdGFibGUgc3VtbWFyaXplcyB0aGUgbnVtZXJpYyB2YXJpYWJsZXMgd2l0aCBhbmQgd2l0aG91dCBJUVIgb3V0bGllcnMgdG8gc2hvdyBob3cgdGhlIGRhdGEgY2hhbmdlcyBiYXNlZCBvbiB0aGlzIGZpbHRlci4KClRPRE8gLSBjaGVjayB0aGUgY29tcG9zaXRpb24gb2YgdGhlIGZlcnRpbGl6ZXIvYWNyZSB2YXJpYWJsZXMuCgpgYGB7cn0Ka25pdHI6OmthYmxlKGlxclRhYiwgcm93Lm5hbWVzID0gRiwgZGlnaXRzID0gMCwgZm9ybWF0ID0gJ21hcmtkb3duJykKYGBgCgojIyMgT3V0bGllciBHcmFwaHMKCmBgYHtyfQojIGh0dHA6Ly9yZm9ycHVibGljaGVhbHRoLmJsb2dzcG90LmNvbS8yMDE0LzAyL2dncGxvdDItY2hlYXRzaGVldC1mb3ItdmlzdWFsaXppbmcuaHRtbApmb3IoaSBpbiAxOmxlbmd0aChudW1WYXJzTm90QWRtaW4pKXsKICAgIGJhc2UgPC0gZ2dwbG90KGQsIGFlcyh4PWRbLG51bVZhcnNOb3RBZG1pbltpXV0pKSArIGxhYnMoeCA9IG51bVZhcnNOb3RBZG1pbltpXSkKICAgIHRlbXAxIDwtIGJhc2UgKyBnZW9tX2RlbnNpdHkoKQogICAgdGVtcDIgPC0gYmFzZSArIGdlb21faGlzdG9ncmFtKCkKICAgICN0ZW1wMiA8LSBib3hwbG90KHJbLG51bVZhcnNbaV1dLG1haW49cGFzdGUwKCJWYXJpYWJsZTogIiwgbnVtVmFyc1tpXSkpCiAgICBtdWx0aXBsb3QodGVtcDEsIHRlbXAyLCBjb2xzID0gMikKfQpgYGAKCiMjIyBDbGVhbiBzb2lsIHZhbHVlcwoKQmVmb3JlIGFsaWduaW5nIHRoZW0gd2l0aCB0aGUgYmFzZWxpbmUgc29pbCB2YWx1ZXMKCmBgYHtyfQpjaGVjay4zc2QgPC0gZnVuY3Rpb24oeCkgewogIHggPSBpZmVsc2UoaXMuaW5maW5pdGUoeCksIE5BLCB4KQogIG1lYW4gPSBtZWFuKHgsIG5hLnJtPVQpCiAgc2QgPSBzZCh4LCBuYS5ybT1UKQogIG1hcmsgPSBpZmVsc2UoeD4obWVhbiArICgzKnNkKSkgfAogICAgICAgIHg8KG1lYW4gLSAoMypzZCkpLCBOQSwgeCkKICByZXR1cm4obWFyaykKfQoKCnNkU29pbFZhbHMgPC0gZCAlPiUKICBkcGx5cjo6c2VsZWN0KHBIOlguVG90YWwuTml0cm9nZW4pIAoKc2RDaGVjayA8LSBhcy5kYXRhLmZyYW1lKGFwcGx5KHNkU29pbFZhbHMsIDIsIGZ1bmN0aW9uKHgpewogIHJldHVybihjaGVjay4zc2QoeCkpCn0pKQpgYGAKCmBgYHtyfQpmb3IoaSBpbiAxOmxlbmd0aChzb2lsVmFycykpewogIHByaW50KGdncGxvdChkYXRhPXNkQ2hlY2ssIGFlcyh4PXNkQ2hlY2tbLHNvaWxWYXJzW2ldXSkpICsgCiAgICBnZW9tX2RlbnNpdHkoKSArIAogICAgbGFicyh4PXNvaWxWYXJzW2ldKQogICkKfQoKYGBgCgoqKkltcG9ydGFudCBub3RlKio6IEknbSBnb2luZyB0byBhZGQgdGhlIGFkanVzdGVkIHZhbHVlcyB0byB0aGUgYHJgIGRhdGEgZnJhbWUgZ2l2aW5nIHRoZSBwcmV2aW91cyB2YXJpYWJsZXMgdGhlIGV4dGVuc2lvbiBgLnJhd2Agc28gSSBjYW4gZGlzdGluZ3Vpc2ggYmV0d2VlbiB0aGUgb3JpZ2luYWwgYW5kIG1vZGlmaWVkIGRhdGEuCgpgYGB7cn0KbmFtZXMoZClbd2hpY2gobmFtZXMoZCk9PSJwSCIpOndoaWNoKG5hbWVzKGQpPT0iWC5Ub3RhbC5OaXRyb2dlbiIpXSA8LSBwYXN0ZTAobmFtZXMoZClbd2hpY2gobmFtZXMoZCk9PSJwSCIpOndoaWNoKG5hbWVzKGQpPT0iWC5Ub3RhbC5OaXRyb2dlbiIpXSwgIi5yYXciKQoKZCA8LSBjYmluZChkLCBzZENoZWNrKQpgYGAKCiMjIENoZWNrIHJvdW5kIDEgaWRzCgpDaGVjayBmb3IgdW5pcXVlIGlkcyBpbiB0aGUgcm91bmQgMSBkYXRhCmBgYHtyfQpsZW5ndGgoZCRTb2lsX1NhbXBsZV9JZCk9PWxlbmd0aCh1bmlxdWUoZCRTb2lsX1NhbXBsZV9JZCkpCmR1cHMgPC0gZCRTb2lsX1NhbXBsZV9JZFtkdXBsaWNhdGVkKGQkU29pbF9TYW1wbGVfSWQpICYgIWlzLm5hKGQkU29pbF9TYW1wbGVfSWQpXQpgYGAKCkxldCdzIHVzZSB0aGUgYmFzZWxpbmUgZGF0YSB0byByZXNvbHZlIHRoZXNlIGR1cGxpY2F0ZWQgaWRzCmBgYHtyfQpyb3VuZElkIDwtIGQgJT4lCiAgZHBseXI6OnNlbGVjdChkaXN0cmljdCwgc2l0ZSwgU29pbF9TYW1wbGVfSWQpICU+JQogIGZpbHRlcihkJFNvaWxfU2FtcGxlX0lkICVpbiUgZHVwcykgJT4lCiAgZmlsdGVyKCFpcy5uYShkaXN0cmljdCkgJiAhaXMubmEoU29pbF9TYW1wbGVfSWQpKQpgYGAKClRoZSBmaXJzdCBvbmUgaXMgbWFya2VkIGFzIGJvdGggT0FGIGFuZCBub3QgT0FGIGluIHRoZSBmb2xsb3cgdXAuIFRoZSBzdXJ2ZXlzIGhhcHBlbmVkIG9uIGNvbnNlY3V0aXZlIGRheXMuIEknbSBnb2luZyB0byBrZWVwIHRoZSBvYmVyc3ZhdGlvbiB3aXRoIHRoZSBzYW1lIHBob25lIG51bWJlciBmcm9tIHRoZSBiYXNlbGluZS4KCmBgYHtyfQojIGR1cGxpY2F0ZWQgaWQgMQppZENoZWNrIDwtIGRbZCRTb2lsX1NhbXBsZV9JZCAlaW4lIGR1cHNbMV0sXQoKYkNoZWNrIDwtIGJbYiRTb2lsX1NhbXBsZV9JZCAlaW4lIGR1cHNbMV0sXQoKZCA8LSBkWy13aGljaChkJFNvaWxfU2FtcGxlX0lkPT1kdXBzWzFdICYgZCRwaG9uZSE9NzI0OTAzMzY2KSxdCmBgYAoKSSB0aGluayB0aGUgc2Vjb25kIHN1cnZleSBtb3JlIGNsb3NlbHkgcmVzZW1ibGVzIHRoZSBiYXNlbGluZSB2ZXJzaW9uIG9mIHRoaXMgZmFybWVyIGluIHRoZSBkYXRhLiBJJ20ga2VlcGluZyB0aGUgc3VydmV5IGRvbmUgYnkgTGF6YXJ1cy4KCmBgYHtyfQojZHVwbGljYXRlZCBpZCAyCmlkQ2hlY2sgPC0gZFtkJFNvaWxfU2FtcGxlX0lkICVpbiUgZHVwc1syXSxdCgpiQ2hlY2sgPC0gYltiJFNvaWxfU2FtcGxlX0lkICVpbiUgZHVwc1syXSxdCgpkIDwtIGRbLXdoaWNoKGQkU29pbF9TYW1wbGVfSWQ9PWR1cHNbMl0gJiBkJG1ldGFkYXRhLnVzZXJuYW1lIT0ibGF6YXJ1cy5tIiksXQoKYGBgCgpUaGUgYWdlIGFuZCB0aGUgR1BTIGNvb3JkaW5hdGUgaW5kaWNhdGUgdGhhdCB0aGUgc3VydmV5IHdpdGggdGhlIGFnZSBvZiA1NCBpcyBwcm9iYWJseSB0aGUgcmlnaHQgb25lLgoKYGBge3J9CiNkdXBsaWNhdGVkIGlkIDMKaWRDaGVjayA8LSBkW2QkU29pbF9TYW1wbGVfSWQgJWluJSBkdXBzWzNdLF0KCmJDaGVjayA8LSBiW2IkU29pbF9TYW1wbGVfSWQgJWluJSBkdXBzWzNdLF0KCmQgPC0gZFstd2hpY2goZCRTb2lsX1NhbXBsZV9JZD09ZHVwc1szXSAmIGQkYWdlIT01NCksXQoKYGBgCgpRdWljayBjaGVjayBvZiB0aGUgd29yay4gVGhlcmUgYXJlIHN0aWxsIGBTb2lsX1NhbXBsZV9JZGAgdGhhdCBhcmUgbWlzc2luZy4gTW9zdCBvZiB0aGVtIGhhdmUgYSBuZXcgc29pbCBzYW1wbGUgaWQgYnV0IG5vdCBhbGwuIAoKVE9ETyAtIGNoZWNrIHdpdGggQ2hhcmxlcyBhcyB0byB3aHkgc29tZW9uZSBtaWdodCBub3QgaGF2ZSBhIGBTb2lsX1NhbXBsZV9JZGAgZnJvbSB0aGUgYmFzZWxpbmUgYnV0IHdvdWxkIGhhdmUgYSBuZXcgYFNvaWxfU2FtcGxlX0lkYCBpbiByb3VuZCAxLiAKClRoZXJlIGFyZSBub3QgdGhhdCBtYW55IG5ldyBzb2lsIHNhbXBsZSBpZHMgc28gcGVyaGFwcyBpdCB3YXMgYSB3YXkgdG8gYWRkIG1vcmUgZmFybWVycyB0byB0aGUgc2FtcGxlPwpgYGB7cn0KbGVuZ3RoKGQkU29pbF9TYW1wbGVfSWQpPT1sZW5ndGgodW5pcXVlKGQkU29pbF9TYW1wbGVfSWQpKQp0YWJsZShpcy5uYShkJFNvaWxfU2FtcGxlX0lkKSkKCiNkW2lzLm5hKGQkU29pbF9TYW1wbGVfSWQpLF0KYGBgCgpGT1IgVEhFIFRJTUUgQkVJTkc6IGRyb3AgdGhvc2UgbWlzc2luZyBhIHNvaWwgc2FtcGxlIGlkIGluIHJvdW5kIDEuCmBgYHtyfQpkIDwtIGRbLXdoaWNoKGlzLm5hKGQkU29pbF9TYW1wbGVfSWQpKSxdCmBgYAoKIyMgQWxpZ24gYmFzZWxpbmUgYW5kIHJvdW5kIDEgdmFycwoKSSdsbCBkbyBhIGNvdXBsZSB0aGluZ3M6CgoqIEFkZCBzZWFzb24gdmFyaWFibGUgZm9yIGV2ZW50dWFsIGByYmluZGAKKiBWYXJpYWJsZSBuYW1lcyB0byBsb3dlcmNhc2UKKiBDaGFuZ2UgYmFzZWxpbmUgdmFyaWFibGUgbmFtZXMgdG8gbWF0Y2ggZm9sbG93IHVwLgoqIHJlc29sdmUgZHVwbGljYXRlcyBpbiB0aGUgYmFzZWxpbmUgZGF0YSAod2hlcmUnZCB0aGUgY29tZSBmcm9tPykKCiMjIyBCYXNlbGluZSBkdXBsaWNhdGVzCmBgYHtyfQpsZW5ndGgoYiRTb2lsX1NhbXBsZV9JZCk9PWxlbmd0aCh1bmlxdWUoYiRTb2lsX1NhbXBsZV9JZCkpCmR1cHMgPC0gYiRTb2lsX1NhbXBsZV9JZFtkdXBsaWNhdGVkKGIkU29pbF9TYW1wbGVfSWQpICYgIWlzLm5hKGIkU29pbF9TYW1wbGVfSWQpXQoKYGBgCgpSZXNvbHZlIHRoZW0gaGVyZS4gVGhleSBhbGwgYXBwZWFyIHRvIGJlIGNvbnRyb2wgdmFsdWVzIHRoYXQgd2VyZSB1c2VkIHR3aWNlLiBDYW4gSSBhc3NpZ24gdGhlbSBhIG5ldyBjb250cm9sIHZhbHVlPyBQcm9iYWJseSBub3QuIFNlZSB3aG8gdGhleSBmb2xsb3dlZCB1cCB3aXRoIHRoZSBmaXJzdCByb3VuZCB0byBrbm93IHdoaWNoIG9uZSB0byBrZWVwLgpgYGB7cn0KI2JbYiRzb2lsX3NhbXBsZV9pZD09ImMzMzAiLF0gIyBub3QgY2xlYXIKI2JbYiRzb2lsX3NhbXBsZV9pZD09ImMzODQiLF0gIyBpdCdzIGV4YWN0bHkgdGhlIHNhbWUsIGRyb3Agb25lCiNiW2Ikc29pbF9zYW1wbGVfaWQ9PSJjMTEyOSIsXSAjIGl0J3MgZXhhY3RseSB0aGUgc2FtZSwgZHJvcCBvbmUKI2JbYiRzb2lsX3NhbXBsZV9pZD09ImM2MjYiLF0gIyBub3QgY2xlYXIKI2JbYiRzb2lsX3NhbXBsZV9pZD09ImM4NTYiLF0gIyBpdCdzIGV4YWN0bHkgdGhlIHNhbWUsIGRyb3Agb25lCgpkcm9wT25lIDwtIGMoImMzODQiLCAiYzExMjkiLCAiYzg1NiIpCmIkbiA8LSBhdmUoMTpsZW5ndGgoYiRTb2lsX1NhbXBsZV9JZCksIGIkU29pbF9TYW1wbGVfSWQsIEZVTiA9IHNlcV9hbG9uZykKYiRkcm9wIDwtIGlmZWxzZShiJG49PTIgJiBiJFNvaWxfU2FtcGxlX0lkICVpbiUgZHJvcE9uZSwgMSwgMCkKYiA8LSBiWy13aGljaChiJGRyb3A9PTEpLF0KICAKZHJvcEZvck5vdyA8LSBjKCJjMzMwIiwgImM2MjYiKSAjIHNlZSBiZWxvdwpgYGAgCgpUT0RPIC0gY29uZmlybSB0aGUgZGF0YSBjbGVhbmluZyBwcm9jZXNzIGF0IHRoZSBiYXNlbGluZSBzbyB0aGF0IHRoZSBzb2lsIGRhdGEgdmFyaWFibGVzIGFyZSBiZWluZyB0cmVhdGVkIHRoZSBzYW1lIHdheSBpbiB0aGUgYmFzZWxpbmUgYW5kIHJvdW5kIDEuIFRoZXkgc2hvdWxkIHByb2JhYmx5IGJlIGNsZWFuZWQgc2VwYXJhdGVseSBidXQgc2hvdWxkIGJlIHRyZWF0ZWQgdGhlIHNhbWUgd2F5LgoKYGBge3J9CmQkc2Vhc29uIDwtICIyMDE2IgpiJHNlYXNvbiA8LSAiMjAxNSIKCm5hbWVzKGQpIDwtIHRvbG93ZXIobmFtZXMoZCkpCm5hbWVzKGIpIDwtIHRvbG93ZXIobmFtZXMoYikpCgojIGNvbnZlcnQgYmFzZWxpbmUgc29pbCB0ZXh0dXJlIHZhcnMgaW50byBvbmUgdmFyaWFibGUKYiA8LSBiICU+JQogIG11dGF0ZShzb2lsLnRleHR1cmUgPSBpZmVsc2UoY2xheV9zb2lsPT0xLCAiY2xheSIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKGxvYW1fc29pbD09MSwgImxvYW0iLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmZWxzZShzYW5keV9zb2lsPT0xLCAic2FuZCIsICJ1bmtub3duIikpKSkKCmJVcGRhdGUgPC0gYiAlPiUgcmVuYW1lKAogIHJlZ2lvbiA9IHJlZ2lvbm5hbWUsCiAgZGlzdHJpY3QgPSBkaXN0cmljdG5hbWUsCiAgc2l0ZSA9IHNpdGVuYW1lLAogIHJlc3BvbmRlbnQuZ2VuZGVyID0gZ2VuZGVyLCAjIGNoZWNrIHRoYXQgdGhpcyBpcyBjb3JyZWN0CiAgcGxvdC5sb2NhdGlvbiA9IGZpZWxkX2xvY2F0aW9uLAogIHBsb3Quc2l6ZSA9IGZpZWxkX3NpemUsCiAgaGFydmVzdGNvbXAuMjAxNiA9IHlpZWxkX2NvbXBhcmF0aXZlLAogIG1haW4uY3JvcCA9IHBsb3RfaW5mb3JtYXRpb24ubWFpbmNyb3AsCiAgbWFpbi5jcm9wLnNwZWNpZnkgPSBwbG90X2luZm9ybWF0aW9uLnNwZWNpZnlfb3RoZXJfbWFpbl9jcm9wLAogIHNlZWQudHlwZSA9IHNlZWR0eXBlLCAKICBzZWVkLmtncyA9IHNlZWRrZ3MsCiAgaW50ZXJjcm9wID0gaW50ZXJjcm9wLmludGVyY3JvcCwKICBpbnRlcmNyb3Auc3BlY2lmeSA9IGludGVyY3JvcC5zcGVjaWZ5X290aGVyX21haW5fY3JvcCwKICBpbnRlcmNyb3Auc2VlZC50eXBlID0gaW50ZXJjcm9wLnNlZWR0eXBlLAogIGludGVyY3JvcC5zZWVkLmtncyA9IGludGVyY3JvcC5zZWVka2dzLAogIGludGVyY3JvcC5oYXJ2ZXN0Y29tcCA9IGludGVyY3JvcC55aWVsZF9jb21wYXJhdGl2ZSwKICBmZXJ0aWxpemVyLm1haW4gPSBpbnB1dHMuaW5wdXRfbWFpbmNyb3AsCiAgZGFwLm1haW4gPSBpbnB1dHMuZGFwX2tnLAogIGNhbi5tYWluID0gaW5wdXRzLmNhbl9rZywKICBucGsubWFpbiA9IGlucHV0cy5ucGtfa2csCiAgdXJlYS5tYWluID0gaW5wdXRzLnVyZWFfa2csCiAgZmVydGlsaXplci5pbnRlcmNyb3AgPSBpbnB1dHMuaW5wdXRfaW50ZXJjcm9wLAogIGRhcC5pbnRlcmNyb3AgPSBpbnB1dHMuZGFwX2tnX2ludGVyY3JvcCwKICBjYW4uaW50ZXJjcm9wID0gaW5wdXRzLmNhbl9rZ19pbnRlcmNyb3AsCiAgbnBrLmludGVyY3JvcCA9IGlucHV0cy5ucGtfa2dfaW50ZXJjcm9wLAogIHVyZWEuaW50ZXJjcm9wID0gaW5wdXRzLnVyZWFfa2dfaW50ZXJjcm9wLAogIGNvbXBvc3Qua2dzID0gaW5wdXRzLmNvbXBvc3QsCiAgY29tcG9zdC5xdWFsaXR5ID0gaW5wdXRzLmNvbXBvc3RfcXVhbGl0eSwKICBjb21wb3N0LnR5cGUgPSBpbnB1dHMuY29tcG9zdF90eXBlLAogIGxpbWUgPSBpbnB1dHMubGltZSwKICBsaW1lLmtncyA9IGlucHV0cy5saW1lX2tnLAogIHNvaWwudHlwZSA9IHNvaWxfdHlwZSwKICBzb2lsLmNvbG9yID0gc29pbF9jb2xvciwKICBlcm9zaW9uID0gYW50aV9lcm9zaW9uLAogIGZpZWxkLmxvY2F0aW9uID0gbG9jYXRpb24sCiAgc2Vhc29ucy5vYWYgPSBzZWFzb25zX29hZiwKICB4LmMuZS5jID0gYy5lLmMsCiAgY29wcGVyID0gY3UsCiAgeC5lYy4uc2FsdHMuID0gZWMsCiAgI3guZXhjaGFuZ2VhYmxlLmFsdW1pbnVtID0gZXhjaC5hbCwKICB4LnBob3NwaG9ydXMuc29ycHRpb24uaW5kZXguLnBzaS4gPSBwc2ksCiAgcG90YXNzaXVtID0gaywKICBtYWduZXNpdW0gPSBtZywKICBtYW5nYW5lc2UgPSBtbiwKICBib3JvbiA9IGIsCiAgY2FsY2l1bSA9IGNhLAogIGlyb24gPSBmZSwKICB4LnNvZGl1bSA9IG5hLAogIHBob3NwaG9ydXMgPSBwLCAKICBzdWxwaHVyID0gcywKICB6aW5jID0gem4sCiAgeC5vcmdhbmljLmNhcmJvbiA9IHRvdGFsLmMsCiAgeC50b3RhbC5uaXRyb2dlbiA9IHRvdGFsLm4sCiAgeC5leGNoYW5nZWFibGUuYWx1bWluaXVtID0gZXhjaC5hbAopCgojIGhlbHBlciB0byBrbm93IHdoYXQgZWxzZSBuZWVkcyB0byBiZSBjaGFuZ2VkCm5hbWVzKGQpWyFuYW1lcyhkKSAlaW4lIG5hbWVzKGJVcGRhdGUpXQpuYW1lcyhiVXBkYXRlKVshbmFtZXMoYlVwZGF0ZSkgJWluJSBuYW1lcyhkKV0KCmBgYAoKVGhlIHZhcmlhYmxlcyB0aGF0IHJlbWFpbiB0byBiZSBhbGlnbmVkIGFyZSB0aGUgc29pbCB2YXJpYWJsZXMgYW5kIGFueSByZXN1bHRpbmcgY2FsdWxhdGlvbnMgc3VjaCBhcyBmZXJ0aWxpemVyIHBlciBhY3JlLiBUaGVzZSBjYWN1bGF0aW9ucyBjYW4gYmUgcmVkb25lIG9uY2UgdGhlIGRhdGEgaXMgY29tYmluZWQuCgpNYWtlIGdyb3VwcyBvZiB2YXJpYWJsZXMgdG8gbWFrZSB0aGVtIGVhc2llciB0byBmaW5kIGxhdGVyCgpgYGB7cn0KaGlzdG9yaWNhbEJhc2VsaW5lIDwtIG5hbWVzKGIpW3doaWNoKG5hbWVzKGIpPT0iY2hlbWljYWxfZmVydGlsaXplcl81Iik6d2hpY2gobmFtZXMoYik9PSJsZWd1bV9pbnRlcmNyb3BfNSIpXQpiYXNlbGluZVNvaWxWYXJzIDwtIG5hbWVzKGIpW3doaWNoKG5hbWVzKGIpPT0iYy5lLmMiKTp3aGljaChuYW1lcyhiKT09InRvdGFsLm4iKV0KYGBgCgojIyBDb21iaW5lIGJhc2VsaW5lIGFuZCByb3VuZCAxCgpgYGB7cn0KZCA8LSBkICU+JQogIG11dGF0ZSgKICAgIHNvaWxfc2FtcGxlX2lkID0gdG9sb3dlcihnc3ViKCIgIiwgIiIsIHNvaWxfc2FtcGxlX2lkKSkKICApCgpjb21tb25WYXJzIDwtIG5hbWVzKGQpW25hbWVzKGQpICVpbiUgbmFtZXMoYlVwZGF0ZSldIAoKd3JpdGUuY3N2KGNvbW1vblZhcnMsIGZpbGU9InZhck5hbWVzZm9yTSZFLmNzdiIpCgptMlRvQWNyZXMgPC0gZnVuY3Rpb24oaW5wdXQsIG1ldGVycykgewogIHJlcyA8LSAoaW5wdXQvbWV0ZXJzKSo0MDQ2CiAgcmV0dXJuKHJlcykKfQoKIyBjaGVjayBzb2lsIHNhbXBsZSBpZCBtYXRjaGVzIGJlZm9yZSByYmluZAp0YWJsZShkJHNvaWxfc2FtcGxlX2lkICVpbiUgYlVwZGF0ZSRzb2lsX3NhbXBsZV9pZCkKZCRzb2lsX3NhbXBsZV9pZFshZCRzb2lsX3NhbXBsZV9pZCAlaW4lIGJVcGRhdGUkc29pbF9zYW1wbGVfaWRdCgp0YWJsZShiVXBkYXRlJHNvaWxfc2FtcGxlX2lkICVpbiUgZCRzb2lsX3NhbXBsZV9pZCkKYlVwZGF0ZSRzb2lsX3NhbXBsZV9pZFshYlVwZGF0ZSRzb2lsX3NhbXBsZV9pZCAlaW4lIGQkc29pbF9zYW1wbGVfaWRdCgpmaWVsZERhdCA8LSByYmluZChiVXBkYXRlWyxjb21tb25WYXJzXSwgZFssY29tbW9uVmFyc10pCgpmaWVsZERhdCA8LSBmaWVsZERhdCAlPiUgCiAgbXV0YXRlKAogIHBsb3QubTIgPSBwbG90LnNpemUqNDA0NiwKICBjYW4uYWNyZSA9IG0yVG9BY3JlcyhjYW4ubWFpbiwgcGxvdC5tMiksCiAgZGFwLmFjcmUgPSBtMlRvQWNyZXMoZGFwLm1haW4scGxvdC5tMiksCiAgbnBrLmFjcmUgPSBtMlRvQWNyZXMobnBrLm1haW4scGxvdC5tMiksCiAgdXJlYS5hY3JlID0gbTJUb0FjcmVzKHVyZWEubWFpbixwbG90Lm0yKSwKICBjb21wb3N0LmFjcmUgPSBtMlRvQWNyZXMoY29tcG9zdC5rZ3MscGxvdC5tMiksCiAgc2VlZC5hY3JlID0gbTJUb0FjcmVzKHNlZWQua2dzLHBsb3QubTIpLAoKICBpbnRlcmNyb3Auc2VlZC5hY3JlID0gbTJUb0FjcmVzKGludGVyY3JvcC5zZWVkLmtncywgcGxvdC5tMiksCiAgaW50ZXJjcm9wLmRhcC5hY3JlID0gbTJUb0FjcmVzKGRhcC5pbnRlcmNyb3AsIHBsb3QubTIpLAogIGludGVyY3JvcC5jYW4uYWNyZSA9IG0yVG9BY3JlcyhjYW4uaW50ZXJjcm9wLCBwbG90Lm0yKSwKICBpbnRlcmNyb3AubnBrLmFjcmUgPSBtMlRvQWNyZXMobnBrLmludGVyY3JvcCwgcGxvdC5tMiksCiAgaW50ZXJjcm9wLnVyZWEuYWNyZSA9IG0yVG9BY3Jlcyh1cmVhLmludGVyY3JvcCwgcGxvdC5tMiksCiAgc29pbC50ZXh0dXJlID0gdG9sb3dlcihzb2lsLnRleHR1cmUpCikgJT4lIHJlbmFtZSgKICBkX2NsaWVudCA9IG9hZiwKICBzYW1wbGVfaWQgPSBzb2lsX3NhbXBsZV9pZAopCgojIGRyb3AgdGhlIGlkcyB0aGF0IGFyZSBkdXBsaWNhdGVzCmZpZWxkRGF0IDwtIGZpZWxkRGF0WyFmaWVsZERhdCRzYW1wbGVfaWQgJWluJSBkcm9wRm9yTm93LF0KCgojIGFuZCBhIGJpdCBtb3JlIHNvaWwgdGV4dHVyZSBjbGVhbmluZwpmaWVsZERhdCA8LSBmaWVsZERhdCAlPiUKICBtdXRhdGUoc29pbC50ZXh0dXJlID0gZ3N1YigiY2FseSIsICJjbGF5Iiwgc29pbC50ZXh0dXJlKSwKICAgICAgICAgc29pbC50ZXh0dXJlID0gZ3N1YigiXFwuIiwgIiIsIHNvaWwudGV4dHVyZSksCiAgICAgICAgIHNvaWwudGV4dHVyZSA9IGdzdWIoInNpbHRheXxzaWxhdHl8c2lsdHkgIiwgInNpbHR5Iiwgc29pbC50ZXh0dXJlKSwKICAgICAgICAgc29pbC50ZXh0dXJlID0gZ3N1YigiICIsICIiLCBzb2lsLnRleHR1cmUpKQpgYGAKCiMjIyBFeHBvcnQgZGF0YSBmb3IgU3RlZmFuIGFuZCBTdGVwCgpKYW51YXJ5IDl0aCwgMjAxOCAtIGZ1bGZpbGxpbmcgcmVxdWVzdCBmb3Igc29pbCBkYXRhIGFuZCBhZGRpdGlvbmFsIHZhcmlhYmxlcwpgYGB7cn0KZmllbGREYXQgJT4lCiAgZmlsdGVyKHNlYXNvbj09IjIwMTYiKSAlPiUKICBkcGx5cjo6c2VsZWN0KHJlZ2lvbiwgZGlzdHJpY3QsIHNpdGUsIHBoLCB4Lm9yZ2FuaWMuY2FyYm9uLCBzb2lsLnRleHR1cmUsIHNvaWwudHlwZSwgc29pbC5jb2xvciwgCiAgICAgICAgIHgudG90YWwubml0cm9nZW4sIGdwcywgZmllbGQubG9jYXRpb24sIGNvbXBvc3Qua2dzLCBjb21wb3N0LnF1YWxpdHksIGNvbXBvc3QudHlwZSkgJT4lCiAgd3JpdGUuY3N2KC4sIGZpbGU9ImNLZURhdGFfZm9yU3RlZmFuYW5kU3RlcC5jc3YiLCByb3cubmFtZXMgPSBGKQpgYGAKCgpDaGVjayB0aGF0IGNhdGVnb3JpY2FsIHZhcmlhYmxlIHJlc3BvbnNlcyBhbGlnbiBiZXR3ZWVuIHRoZSBiYXNlbGluZSBhbmQgcm91bmQgMS4KCmBgYHtyfQpjYXRWYXJzIDwtIG5hbWVzKGZpZWxkRGF0KVtzYXBwbHkoZmllbGREYXQsIGZ1bmN0aW9uKHgpewogIGlzLmNoYXJhY3Rlcih4KQp9KV0KCmZpZWxkTm9uQWRtaW4gPC0gY2F0VmFyc1shY2F0VmFycyAlaW4lIGMoInBsb3QubG9jYXRpb24iLCAiZ3BzIiwgInNhbXBsZV9pZCIsICJtYWluLmNyb3Auc3BlY2lmeSIsICJpbnRlcmNyb3Auc3BlY2lmeSIsICJwaG90byIsICJzaXRlIildCgpmaWVsZERhdCRyZWdpb24gPC0gaWZlbHNlKGdyZXBsKCJueWFuIiwgdG9sb3dlcihmaWVsZERhdCRyZWdpb24pKSwgIk55YW56YSIsICJXZXN0ZXJuIikKCgpmb3IoaSBpbiAxOmxlbmd0aChmaWVsZE5vbkFkbWluKSl7CiAgcmVwR3JhcGhzKGZpZWxkRGF0LCBmaWVsZE5vbkFkbWluW2ldKQp9CgpgYGAKClRPRE8KCiogcmVhbGl0eSBjaGVjayB0aGUgZGF0YSBjbGVhbmluZyBhbmQgbWFuYWdlbWVudCBwcm9jZXNzIGJldHdlZW4gYmFzZWxpbmUgYW5kIHJvdW5kIDEsIG1ha2Ugc3VyZSB0aGV5J3JlIHRoZSBzYW1lLgoqIHJlYWxpdHkgY2hlY2sgdGhlIGFwcGVuZAoqIHJlYWxpdHkgY2hlY2sgR1BTCgojIEFuYWx5c2lzCgpUaGUgYWltIG9mIHRoZSBhbmFseXNpcyBpcyB0bzogCgpUT0RPCgoqIENoZWNrIG1vdmVtZW50IGluIHRoZSBzYW1wbGUKKiByZXNvbHZlIGR1cGxpY2F0ZXMgaW4gdGhlIGJhc2VsaW5lIChzZWUgYmVsb3chKQoqIGN1dCBzdHJhaWdodCB0byB0aGUgdGFibGVzIGluIHRoZSByZXBvcnQgYWxhIFJ3YW5kYQoKIyMgQ2xpZW50IG1vdmVtZW50CgpgYGB7cn0KZmllbGREYXQgJT4lCiAgZHBseXI6OnNlbGVjdChzYW1wbGVfaWQsIHNlYXNvbiwgZF9jbGllbnQpICU+JQogIGdyb3VwX2J5KHNhbXBsZV9pZCkgJT4lCiAgc3ByZWFkKC4sIHNlYXNvbiwgZF9jbGllbnQpICU+JQogIHJlbmFtZSgKICAgIGNsaWVudDE1ID0gYDIwMTVgLAogICAgY2xpZW50MTYgPSBgMjAxNmAKICApICU+JQogIG11dGF0ZSgKICAgIGJlY2FtZUNsaWVudCA9IGlmZWxzZShjbGllbnQxNT09MCAmIGNsaWVudDE2PT0xLCAxLCAwKSwKICAgIGJlY2FtZUNvbnRyb2wgPSBpZmVsc2UoY2xpZW50MTU9PTEgJiBjbGllbnQxNj09MCwgMSwgMCksCiAgICBzdGF5ZWRDbGllbnQgPSBpZmVsc2UoY2xpZW50MTU9PTEgJiBjbGllbnQxNj09MSwgMSwgMCksCiAgICBzdGF5ZWRDb250cm9sID0gaWZlbHNlKGNsaWVudDE1PT0wICYgY2xpZW50MTY9PTAsIDEsIDApCiAgKSAlPiUgCiAgdW5ncm91cCgpICU+JQogIGRwbHlyOjpzdW1tYXJpemVfZWFjaCgKICAgIGZ1bnMobWVhbj0gbWVhbiguLCBuYS5ybT1UKSksIC1jKHNhbXBsZV9pZCwgY2xpZW50MTUsIGNsaWVudDE2KQogICkgJT4lIAogIG11dGF0ZV9lYWNoKAogICAgZnVucyhwYXN0ZTAocm91bmQoLiwyKSoxMDAsICIlIikpCiAgKSAlPiUKICBrYWJsZShjYXB0aW9uPSJNb3ZlbWVudCBpbiBTYW1wbGUiLCBmb3JtYXQ9J21hcmtkb3duJykKCmBgYAoKIyMgQ2xpZW50IGNvdW50cwoKYGBge3J9CmNsaWVudENvdW50IDwtIGZpZWxkRGF0ICU+JQogIGRwbHlyOjpzZWxlY3Qoc2FtcGxlX2lkLCBzZWFzb24sIGRfY2xpZW50KSAlPiUKICBncm91cF9ieShzYW1wbGVfaWQpICU+JQogIHNwcmVhZCguLCBzZWFzb24sIGRfY2xpZW50KSAlPiUKICByZW5hbWUoCiAgICBjbGllbnQxNSA9IGAyMDE1YCwKICAgIGNsaWVudDE2ID0gYDIwMTZgCiAgKQoKCmNsaWVudENvdW50VGFiIDwtIGNiaW5kKAogIGFzLmRhdGEuZnJhbWUodGFibGUoY2xpZW50Q291bnQkY2xpZW50MTUsIHVzZU5BID0gJ2lmYW55JykpLAogIGFzLmRhdGEuZnJhbWUodGFibGUoY2xpZW50Q291bnQkY2xpZW50MTYsIHVzZU5BID0gJ2lmYW55JykpKQoKY2xpZW50Q291bnRUYWIgPC0gY2xpZW50Q291bnRUYWJbLC0zXQpuYW1lcyhjbGllbnRDb3VudFRhYikgPC0gYygiVHJlYXRtZW50IiwgIkNsaWVudHMgMjAxNSIsICJDbGllbnRzIDIwMTYiKQp3cml0ZS5jc3YoY2xpZW50Q291bnRUYWIsIGZpbGU9cGFzdGUwKCJvdXRwdXQvIiwgImNsaWVudENvdW50VGFiLmNzdiIpLCByb3cubmFtZXMgPSBGKQpgYGAKCgojIyMgUmVncmVzc2lvbnMKClNlZSBbc2tldGNoIG9mIFNIUyByZXBvcnRdKGh0dHBzOi8vZG9jcy5nb29nbGUuY29tL2RvY3VtZW50L2QvMWtvTnNLeng5N18zcnBrR2VKSTZQblBkWU5NZFY5cTRlLWNQUXhBV0RlRGsvZWRpdCkuICAKClRPRE8gLSBTVEFSVCBIRVJFCgoqIHJ1biByZWdyZXNzaW9uCiogYnJpbmcgaW4geWllbGQgdmFsdWVzLCBtZXJnZSBzb2lsIHZhbGVzLCBmb2xsb3cgdGhlIG90aGVyIHRlbXBsYXRlCiogcHJvZHVjZSByZW1haW5pbmcgdGFibGVzCgpDb25zaWRlciBpbmNsdWRpbmc6CgoqIHRpbWUgRkVzCiogYWdlIGFuZCBhIHNxdWFyZWQgYWdlIHRlcm0KKiBnZW5kZXIgKGFic29yYmVkIGJ5IGZpeGVkIGVmZmVjdHMpCiogeWVhcnMgb2YgZWR1Y2F0aW9uIChhYnNvcmJlZCBieSBmaXhlZCBlZmZlY3RzKQoqIGJvb3RzdHJhcHBlZCBzdC4gZXJyb3JzIC8gcm9idXN0IHN0YW5kYXJkIGVycm9ycwoKW0hlbHBmdWwgbGlua10oaHR0cHM6Ly93d3cuci1ibG9nZ2Vycy5jb20vaG93LXRvLWdvLXBhcmFsbGVsLWluLXItYmFzaWNzLXRpcHMvKSBmb3IgZXhlY3V0aW5nIGNvZGUgaW4gcGFyYWxsZWwKCmBgYHtyfQpzb3VyY2UoIi4uL29hZmxpYi9wbG0uUiIpCmZpZWxkU29pbERhdCA8LSBmaWVsZERhdCAlPiUKICBtdXRhdGUoCiAgICBhZ2UyID0gYWdlXjIKICApCgprZXlTb2lsVmFycyA8LSBjKCJwaCIsICJjYWxjaXVtIiwgIm1hZ25lc2l1bSIsICJ4Lm9yZ2FuaWMuY2FyYm9uIiwgIngudG90YWwubml0cm9nZW4iKQoKaW5kRmVMaXN0IDwtIGxpc3QoImFzLmZhY3RvcihkX2NsaWVudCkiLCAKICAgICAgICAgICAgICAgICAgYygiYXMuZmFjdG9yKGRfY2xpZW50KSIsICJhcy5mYWN0b3Ioc2FtcGxlX2lkKSIpLAogICAgICAgICAgICAgICAgICBjKCJhcy5mYWN0b3IoZF9jbGllbnQpIiwgImFzLmZhY3RvcihzYW1wbGVfaWQpIiwgImFzLmZhY3RvcihzZWFzb24pIiksCiAgICAgICAgICAgICAgICAgIGMoImFzLmZhY3RvcihkX2NsaWVudCkiLCAiYXMuZmFjdG9yKHNhbXBsZV9pZCkiLCAiYXMuZmFjdG9yKHNlYXNvbikiLCAiYWdlIiwgImFnZTIiKSkKCgojIHJ1biB0aGlzIGluIHBhcmFsbGVsIHRvIHNwZWVkIHVwIHRoZSBwcm9jZXNzCiMgbG9hZCB0aGUgZGF0YSBhbmQgdmFyaWFibGVzIGFuZCBwYWNrYWdlcyBpbnRvIHRoZSBjbHVzdGVyCnJlZ0ZpbGUgPC0gInJlZ0ZpbGUuUkRhdGEiCiNmb3JjZVVwZGF0ZSA8LSBmb3JjZVVwZGF0ZUFsbAppZighZmlsZS5leGlzdHMocmVnRmlsZSkgfHwgZm9yY2VVcGRhdGUpIHsKbGlicmFyeShwYXJhbGxlbCkKbm9fY29yZXMgPC0gZGV0ZWN0Q29yZXMoKSAtIDEKCmNsIDwtIG1ha2VDbHVzdGVyKG5vX2NvcmVzLCB0eXBlPSJGT1JLIikKY2x1c3RlckV2YWxRKGNsLCAicGxtIikKY2x1c3RlckV4cG9ydChjbCwgImZpZWxkU29pbERhdCIpCmNsdXN0ZXJFeHBvcnQoY2wsICJrZXlTb2lsVmFycyIpCmNsdXN0ZXJFeHBvcnQoY2wsICJpbmRGZUxpc3QiKQoKaW5kRmVMb29wIDwtIHBhckxhcHBseShjbCwgaW5kRmVMaXN0LCBmdW5jdGlvbihtb2QpewogIGxhcHBseShrZXlTb2lsVmFycywgZnVuY3Rpb24ob3V0Y29tZSl7CiAgICBmb3JtID0gbG0ocmVmb3JtdWxhdGUodGVybWxhYmVscyA9IG1vZCwgcmVzcG9uc2UgPSBvdXRjb21lKSwgZGF0YT1maWVsZFNvaWxEYXQpCiAgICAKICAgIHBkZihmaWxlPXBhc3RlKCJvdXRwdXQvIiwgcGFzdGUwKG91dGNvbWUsIHBhc3RlKG1vZCwgY29sbGFwc2UgPSAiIikpLCAiLnBkZiIsIHNlcCA9ICIiKSkgCiAgICBwcmludChwbG90KGZvcm0pKQogICAgZGV2Lm9mZigpCiAgICAKICAgIGZvcm0gPSBwbG0oZm9ybSwgYygic2FtcGxlX2lkIiwgImFnZSIsICJhZ2UyIikpCiAgICAKICAgIHJvd25hbWVzKGZvcm0pID0gcGFzdGUocm93bmFtZXMoZm9ybSksIG91dGNvbWUsIHNlcCA9ICIgIikKICAgIHJldHVybihmb3JtKQogIH0pCiAgCn0pCnN0b3BDbHVzdGVyKGNsKQpzYXZlKGluZEZlTG9vcCwgZmlsZT1yZWdGaWxlKQp9IGVsc2UgewogIGxvYWQoInJlZ0ZpbGUuUkRhdGEiKQp9CmBgYAoKQW5kIGNvbWJpbmUgbW9kZWwgb3V0cHV0cyBpbnRvIHRhYmxlcyBmb3IgZWFjaCBtb2RlbAoKYGBge3J9Cm1vZEV4cG9ydCA8LSBsYXBwbHkoaW5kRmVMb29wLCBmdW5jdGlvbihtb2RlbHMpewogIGRvLmNhbGwocmJpbmQsIG1vZGVscykKfSkKCmZvcihpIGluIDE6bGVuZ3RoKG1vZEV4cG9ydCkpewogIHdyaXRlLmNzdihtb2RFeHBvcnRbaV0sIGZpbGU9cGFzdGUwKCJvdXRwdXQvIiwicmVnT3V0cHV0XyIsIGksICIuY3N2IiksIHJvdy5uYW1lcyA9IFQpCn0KCmBgYAoKSW4gdGhlIGluZGl2aWR1YWwgZml4ZWQgZWZmZWN0IG1vZGVsIGFib3ZlLCB0aGUgbmFpdmUgbW9kZWwgd291bGQgb25seSBpbmNsdWRlIGEgY2xpZW50IGluZGljYXRvciBhbmQgaW5kaXZpZHVhbCBmaXhlZCBlZmZlY3RzLiBJZiB3ZSBhZGQgc2Vhc29uLCB3ZSBsb3NlIHNpZ25pZmljYW5jZSBvbiBhbG1vc3QgZXZlcnl0aGluZy4gSSdkIGd1ZXNzIHRoYXQgYXMgd2UgYWRkIG1vcmUgbGlrZWx5IGNvbnRyb2xzIHdlIGFkZGl0aW9uYWxseSBsb3NlIHNpZ25maWNhbmNlLiBJJ3ZlIGluY2x1ZGVkIGFnZSBhbmQgYWdlIHNxdWFyZWQgYWxvbmcgdGhlIGxpbmVzIG9mIFtIaWNrcyBldC5hbF0oaHR0cDovL3d3dy5uYmVyLm9yZy9wYXBlcnMvdzIzMjUzKS4KCgpgYGB7cn0KZmluYWxNb2RlbCA8LSBtb2RFeHBvcnRbNF0KCmthYmxlKGZpbmFsTW9kZWwsIGZvcm1hdD0ibWFya2Rvd24iKQp3cml0ZS5jc3YoZmluYWxNb2RlbCwgZmlsZT0ib3V0cHV0L2luZEZlLmNzdiIpCmBgYAoKCiMgU3VtbWFyeQoKCgoKCgo=